rollup-plugin-tsx-scoped-css
Version:
134 lines (133 loc) • 5.36 kB
JavaScript
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
(function (factory) {
if (typeof module === "object" && typeof module.exports === "object") {
var v = factory(require, exports);
if (v !== undefined) module.exports = v;
}
else if (typeof define === "function" && define.amd) {
define(["require", "exports", "@vue/component-compiler-utils", "fs", "md5", "path", "ts-morph"], factory);
}
})(function (require, exports) {
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const component_compiler_utils_1 = require("@vue/component-compiler-utils");
const fs_1 = __importDefault(require("fs"));
const md5_1 = __importDefault(require("md5"));
const path_1 = __importDefault(require("path"));
const ts_morph_1 = require("ts-morph");
const project = new ts_morph_1.Project({
tsConfigFilePath: findTsConfig(),
});
const hash_cache = {};
function default_1(raw_options = {}) {
const options = {
isScopedCssFile,
...raw_options.isScopedCssFile,
};
return {
name: 'rollup-plugin-tsx-scoped-css',
enforce: 'pre',
transform(code, filepath) {
// 处理 css 文件
if (options.isScopedCssFile(filepath))
return handleCssFile(code, filepath);
// 处理 jsx/tsx 文件
if (filepath.endsWith('sx'))
return handleTsxFile(code, filepath, options);
return code;
},
};
}
exports.default = default_1;
function findTsConfig() {
let root = process.cwd();
do {
const config_filepath = path_1.default.join(root, 'tsconfig.json');
if (fs_1.default.existsSync(config_filepath)) {
return config_filepath;
}
root = path_1.default.normalize(path_1.default.join(root, '..'));
} while (root !== '/');
}
function handleTsxFile(code, filepath, options) {
const file = project.addSourceFileAtPath(filepath);
const imports = file.getImportDeclarations().filter(_import => {
return options.isScopedCssFile(_import.getModuleSpecifierValue());
});
// 如果没有 import scoped css 文件,则不需要处理
if (imports.length === 0) {
return code;
}
const hashes = [];
imports.forEach(_import => {
const module_path = _import.getModuleSpecifierValue();
let real_path = '';
// 没有使用 paths
if (module_path.startsWith('.')) {
real_path = path_1.default.resolve(path_1.default.join(path_1.default.dirname(filepath), module_path));
}
else {
const tsconfig = project.getCompilerOptions();
if (tsconfig.paths) {
const aliases = { ...tsconfig.paths };
Object.keys(aliases).forEach(source => {
for (let target of aliases[source]) {
target = path_1.default.resolve(path_1.default.join(tsconfig.baseUrl, target));
real_path = module_path.replace(source.replace('*', ''), target.replace('*', ''));
if (fs_1.default.existsSync(real_path)) {
break;
}
}
});
}
}
if (fs_1.default.existsSync(real_path)) {
const hash = getFilepathHash(real_path);
hashes.push(hash);
}
});
const jsx_elements = [
...file.getDescendantsOfKind(ts_morph_1.SyntaxKind.JsxOpeningElement),
...file.getDescendantsOfKind(ts_morph_1.SyntaxKind.JsxSelfClosingElement),
];
// 遍历每一个 jsx 元素
const hash_attrs = hashes.map(hash => `data-v-${hash}`);
jsx_elements
.filter((jsx_element => jsx_element.getFullText().trim() !== '<React.Fragment>'))
.forEach((jsx_element) => {
hash_attrs.forEach(hash => {
jsx_element.addAttribute({ name: hash, initializer: '""' });
});
});
const result_code = file.getFullText();
project.removeSourceFile(file);
return result_code;
}
function handleCssFile(source, filepath) {
const hash = getFilepathHash(filepath);
const { code, errors } = component_compiler_utils_1.compileStyle({
source,
filename: filepath,
id: `data-v-${hash}`,
scoped: true,
trim: true,
});
if (errors.length) {
console.error(errors[0]);
}
return code;
}
function isScopedCssFile(filepath) {
return /\.scoped\.(c|le|sa|sc)ss$/.test(filepath);
}
function getFilepathHash(filepath) {
filepath = path_1.default.resolve(filepath);
let hash = hash_cache[filepath];
if (!hash) {
hash_cache[filepath] = hash = md5_1.default(filepath).substr(0, 8);
}
return hash;
}
});