/
plugin.js
109 lines (87 loc) · 2.78 KB
/
plugin.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
/* @flow */
import {
dirname,
extname,
resolve,
join,
} from 'path';
import {
execFileSync,
spawn,
} from '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]/g, '');
const socketName = `bptp-${projectId}.sock`;
const socketPath = join('/tmp', socketName);
const nodeExecutable = process.argv[0];
const clientExcutable = join(__dirname, 'postcss-client.js');
const serverExcutable = join(__dirname, 'postcss-server.js');
let server;
const startServer = () => {
server = spawn(nodeExecutable, [serverExcutable, socketPath], {
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);
};
export default function transformPostCSS({ types: t }: any): any {
const extensions = ['.css'];
return {
visitor: {
CallExpression(path: any, { file }: any) {
const { callee: { name: calleeName }, arguments: args } = path.node;
if (calleeName !== 'require' ||
!args.length ||
!t.isStringLiteral(args[0])) {
return;
}
const [{ value: stylesheetPath }] = args;
const stylesheetExtension = extname(stylesheetPath);
if (extensions.indexOf(stylesheetExtension) !== -1) {
launchServer();
const requiringFile = file.opts.filename;
const cssFile = resolve(dirname(requiringFile), stylesheetPath);
const data = JSON.stringify({ cssFile });
const execArgs = [clientExcutable, socketPath, data];
const result = execFileSync(nodeExecutable, execArgs, {
env: process.env, // eslint-disable-line no-process-env
}).toString('utf8');
const tokens = JSON.parse(result || '{}');
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])
)
)
));
}
},
},
};
}
export {
startServer,
stopServer,
};