@joblift/babel-plugin-transform-postcss
Version:
PostCSS Babel Transform
129 lines (97 loc) • 4.11 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.stopServer = exports.startServer = undefined;
exports.default = transformPostCSS;
var _path = require('path');
var _child_process = require('child_process');
// note: socket path is important to keep short as it will be truncated if it
// exceeds certain platform limits. for this reason, we're writing to /tmp
// instead of using os.tmpdir (which can, on platforms like darwin, be quite
// long & per-process).
const projectId = process.cwd().toLowerCase().replace(/[^a-z]/ig, '');
const socketName = `bptp-${projectId}.sock`;
const socketPath = (0, _path.join)('/tmp', socketName);
const tmpPath = (0, _path.join)('/tmp', `bptp-${projectId}`);
const nodeExecutable = process.argv[0];
const clientExcutable = (0, _path.join)(__dirname, 'postcss-client.js');
const serverExcutable = (0, _path.join)(__dirname, 'postcss-server.js');
let server;
const startServer = () => {
server = (0, _child_process.spawn)(nodeExecutable, [serverExcutable, socketPath, tmpPath], {
env: process.env, // eslint-disable-line no-process-env
stdio: 'inherit'
});
server.unref();
};
const stopServer = () => {
if (!server) {
return;
}
server.kill();
server = null;
process.removeListener('exit', stopServer);
};
const launchServer = () => {
if (server) {
return;
}
startServer();
process.on('exit', stopServer);
};
const defaultExtensions = ['.css'];
const getStylesFromStylesheet = (stylesheetPath, file, config, configExtensions) => {
const stylesheetExtension = (0, _path.extname)(stylesheetPath);
const extensions = Array.isArray(configExtensions) ? configExtensions : defaultExtensions;
if (extensions.indexOf(stylesheetExtension) !== -1) {
launchServer();
const requiringFile = file.opts.filename;
const cssFile = (0, _path.resolve)((0, _path.dirname)(requiringFile), stylesheetPath);
const data = JSON.stringify({ cssFile, config });
const execArgs = [clientExcutable, socketPath, data];
const result = (0, _child_process.execFileSync)(nodeExecutable, execArgs, {
env: process.env // eslint-disable-line no-process-env
}).toString();
return JSON.parse(result || '{}');
}
return undefined;
};
function transformPostCSS({ types: t }) {
return {
visitor: {
CallExpression(path, { file }) {
const { callee: { name: calleeName }, arguments: args } = path.node;
if (calleeName !== 'require' || !args.length || !t.isStringLiteral(args[0])) {
return;
}
const [{ value: stylesheetPath }] = args;
const { config, extensions } = this.opts;
const tokens = getStylesFromStylesheet(stylesheetPath, file, config, extensions);
if (tokens !== undefined) {
const expression = path.findParent(test => test.isVariableDeclaration() || test.isExpressionStatement());
expression.addComment('trailing', ` @related-file ${stylesheetPath}`, true);
path.replaceWith(t.objectExpression(Object.keys(tokens).map(token => t.objectProperty(t.stringLiteral(token), t.stringLiteral(tokens[token])))));
}
},
ImportDeclaration(path, { file }) {
const stylesheetPath = path.node.source.value;
if (path.node.specifiers.length !== 1) {
return;
}
const { config, extensions } = this.opts;
const tokens = getStylesFromStylesheet(stylesheetPath, file, config, extensions);
if (tokens) {
const styles = t.objectExpression(Object.keys(tokens).map(token => t.objectProperty(t.stringLiteral(token), t.stringLiteral(tokens[token]))));
/* eslint-disable new-cap */
const variableDeclaration = t.VariableDeclaration('var', [t.VariableDeclarator(path.node.specifiers[0].local, styles)]);
/* eslint-enable new-cap */
path.addComment('trailing', ` @related-file ${stylesheetPath}`, true);
path.replaceWith(variableDeclaration);
}
}
}
};
}
exports.startServer = startServer;
exports.stopServer = stopServer;