|
1 | | -import * as childProcess from 'child_process' |
2 | | -import { Range } from 'vscode-languageserver' |
| 1 | +import * as crypto from 'crypto' |
| 2 | +import * as BabelTypes from '@babel/types' |
| 3 | +import { Visitor, NodePath } from '@babel/traverse' |
| 4 | +import { dirname } from 'path' |
| 5 | +import { resolveConfig } from '../../class-names' |
| 6 | +const babel = require('@babel/register') |
3 | 7 |
|
4 | | -let fork: childProcess.ChildProcess |
5 | | -let id = 0 |
| 8 | +const LOCATION_PROP_PREFIX = '__twlsp_locations__' |
| 9 | + |
| 10 | +interface PluginOptions { |
| 11 | + file: { |
| 12 | + opts: { |
| 13 | + filename: string |
| 14 | + } |
| 15 | + } |
| 16 | +} |
6 | 17 |
|
7 | 18 | export default function getConfigWithLocations( |
8 | | - configPath: string, |
9 | | - key?: string[] |
10 | | -): Promise<ConfigLocation> { |
11 | | - if (!fork) { |
12 | | - fork = childProcess.fork(__filename, ['--config-locations']) |
| 19 | + configPath: string |
| 20 | +): { config: any } { |
| 21 | + babel.default({ |
| 22 | + plugins: [plugin], |
| 23 | + cache: false, |
| 24 | + babelrc: false, |
| 25 | + ignore: [ |
| 26 | + (filename: string) => { |
| 27 | + if (/node_modules\/@?tailwind/.test(filename)) return false |
| 28 | + return /node_modules/.test(filename) |
| 29 | + }, |
| 30 | + ], |
| 31 | + }) |
| 32 | + |
| 33 | + let config |
| 34 | + |
| 35 | + try { |
| 36 | + // @ts-ignore |
| 37 | + config = resolveConfig({ |
| 38 | + cwd: dirname(configPath), |
| 39 | + config: configPath, |
| 40 | + }) |
| 41 | + } finally { |
| 42 | + babel.revert() |
13 | 43 | } |
14 | 44 |
|
15 | | - let msgId = id++ |
| 45 | + return { config } |
| 46 | +} |
| 47 | + |
| 48 | +function isObjectPropertyPath( |
| 49 | + path: NodePath<any>, |
| 50 | + t: typeof BabelTypes |
| 51 | +): path is NodePath<BabelTypes.ObjectProperty> { |
| 52 | + return t.isObjectProperty(path) |
| 53 | +} |
16 | 54 |
|
17 | | - return new Promise((resolve, reject) => { |
18 | | - function callback(msg: ConfigLocation) { |
19 | | - if (msg.id !== msgId) return |
20 | | - if ('error' in msg) { |
21 | | - fork.off('message', callback) |
22 | | - return reject(msg.error) |
| 55 | +function plugin({ |
| 56 | + types: t, |
| 57 | +}: { |
| 58 | + types: typeof BabelTypes |
| 59 | +}): { visitor: Visitor<PluginOptions> } { |
| 60 | + const objVisitor: Visitor<PluginOptions> = { |
| 61 | + ObjectExpression(path, state) { |
| 62 | + if ( |
| 63 | + isObjectPropertyPath(path.parentPath, t) && |
| 64 | + path.parentPath.node.key.name && |
| 65 | + path.parentPath.node.key.name.startsWith(LOCATION_PROP_PREFIX) |
| 66 | + ) { |
| 67 | + return |
23 | 68 | } |
24 | | - fork.off('message', callback) |
25 | | - fork.kill() |
26 | | - fork = undefined |
27 | | - resolve(msg) |
28 | | - } |
| 69 | + let props = path.node.properties |
| 70 | + .filter( |
| 71 | + (x): x is BabelTypes.ObjectProperty => x.type === 'ObjectProperty' |
| 72 | + ) |
| 73 | + .map((prop) => { |
| 74 | + return t.objectProperty( |
| 75 | + prop.key, |
| 76 | + t.arrayExpression([ |
| 77 | + t.stringLiteral(state.file.opts.filename), |
| 78 | + t.numericLiteral(prop.key.loc.start.line - 1), |
| 79 | + t.numericLiteral(prop.key.loc.start.column), |
| 80 | + t.numericLiteral(prop.key.loc.end.line - 1), |
| 81 | + t.numericLiteral(prop.key.loc.end.column), |
| 82 | + ]), |
| 83 | + prop.computed |
| 84 | + ) |
| 85 | + }) |
| 86 | + if (props.length === 0) return |
| 87 | + path.unshiftContainer( |
| 88 | + 'properties', |
| 89 | + t.objectProperty( |
| 90 | + t.identifier( |
| 91 | + LOCATION_PROP_PREFIX + crypto.randomBytes(16).toString('hex') |
| 92 | + ), |
| 93 | + t.objectExpression(props) |
| 94 | + ) |
| 95 | + ) |
| 96 | + }, |
| 97 | + } |
29 | 98 |
|
30 | | - fork.on('message', callback) |
31 | | - fork.send([msgId, configPath, key]) |
32 | | - }) |
| 99 | + return { |
| 100 | + visitor: { |
| 101 | + Program(path, state) { |
| 102 | + path.traverse(objVisitor, { |
| 103 | + file: { |
| 104 | + opts: { |
| 105 | + filename: state.file.opts.filename, |
| 106 | + }, |
| 107 | + }, |
| 108 | + }) |
| 109 | + }, |
| 110 | + }, |
| 111 | + } |
33 | 112 | } |
34 | | - |
35 | | -export type ConfigLocation = |
36 | | - | { |
37 | | - id: number |
38 | | - key: string[] |
39 | | - file: string |
40 | | - range: Range |
41 | | - } |
42 | | - | { id: number; error: string } |
43 | | - | { id: number; config: any } |
|
0 commit comments