ts-transform-css-modules
Version:
Extract CSS classnames in TS files
104 lines (103 loc) • 3.86 kB
JavaScript
;
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0)
t[p[i]] = s[p[i]];
return t;
};
Object.defineProperty(exports, "__esModule", { value: true });
const ts = require("typescript");
const path_1 = require("path");
const fs_1 = require("fs");
const CSS_EXTENSION_REGEX = /\.css['"]$/;
function resolveCssPath(cssPath, sf, tsImportResolver) {
// Bc cssPath includes ' or "
cssPath = cssPath.substring(1, cssPath.length - 1);
let resolvedPath;
if (typeof tsImportResolver === "function") {
resolvedPath = tsImportResolver(cssPath);
}
if (typeof resolvedPath !== "string") {
if (cssPath.startsWith(".")) {
const sourcePath = sf.fileName;
return path_1.resolve(path_1.dirname(sourcePath), cssPath);
}
return cssPath;
}
return resolvedPath;
}
function generateClassNameObj(resolvedCssPath) {
const css = require(resolvedCssPath);
return ts.createObjectLiteral(ts.createNodeArray(Object.keys(css).map(k => ts.createPropertyAssignment(ts.createLiteral(k), ts.createLiteral(css[k])))));
}
function importVisitor(resolvedCssPath, node) {
let classNameObj;
try {
classNameObj = generateClassNameObj(resolvedCssPath);
}
catch (e) {
console.error(e);
return;
}
// No import clause, skip
if (!node.importClause) {
return;
}
// This is the "foo" from "import * as foo from 'foo.css'"
const { namedBindings } = node.importClause;
// Dealing with "import * as css from 'foo.css'" only since namedImports variables get mangled
if (!ts.isNamespaceImport(namedBindings)) {
return;
}
const importVar = namedBindings.name.getText();
// Create 'var css = {}'
return ts.createVariableStatement(undefined, ts.createVariableDeclarationList([
ts.createVariableDeclaration(importVar, undefined, classNameObj)
]));
}
function visitor(ctx, sf, tsImportResolver) {
const visitor = (node) => {
let newNode;
let cssPath;
if (ts.isImportDeclaration(node)) {
if (CSS_EXTENSION_REGEX.test(node.moduleSpecifier.getText())) {
cssPath = resolveCssPath(node.moduleSpecifier.getText(), sf, tsImportResolver);
newNode = importVisitor(cssPath, node);
}
}
else if (ts.isCallExpression(node)) {
if (node.expression.getText() === "require" &&
CSS_EXTENSION_REGEX.test(node.arguments[0].getText())) {
cssPath = resolveCssPath(node.arguments[0].getText(), sf, tsImportResolver);
try {
newNode = generateClassNameObj(cssPath);
}
catch (e) {
console.error(e);
}
}
}
if (newNode) {
const externalCssSource = ts.createSourceMapSource(cssPath, fs_1.readFileSync(cssPath, "utf-8"));
ts.setSourceMapRange(newNode, {
source: externalCssSource,
pos: node.pos,
end: node.end
});
return newNode;
}
return ts.visitEachChild(node, visitor, ctx);
};
return visitor;
}
function default_1(opts = {}) {
const { tsImportResolver } = opts, hookOpts = __rest(opts, ["tsImportResolver"]);
require("css-modules-require-hook")(hookOpts);
return (ctx) => {
return (sf) => ts.visitNode(sf, visitor(ctx, sf, tsImportResolver));
};
}
exports.default = default_1;