dismantle 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +7 -0
- package/README.md +40 -0
- package/dist/cjs/development/index.cjs +1484 -0
- package/dist/cjs/development/index.cjs.map +7 -0
- package/dist/cjs/development/runtime.cjs +1484 -0
- package/dist/cjs/development/runtime.cjs.map +7 -0
- package/dist/cjs/production/index.cjs +1 -0
- package/dist/cjs/production/runtime.cjs +1 -0
- package/dist/esm/development/index.mjs +1453 -0
- package/dist/esm/development/index.mjs.map +7 -0
- package/dist/esm/development/runtime.mjs +1453 -0
- package/dist/esm/development/runtime.mjs.map +7 -0
- package/dist/esm/production/index.mjs +1 -0
- package/dist/esm/production/runtime.mjs +1 -0
- package/dist/types/hidden-imports.d.ts +4 -0
- package/dist/types/hidden-imports.d.ts.map +1 -0
- package/dist/types/index.d.ts +9 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/plugin.d.ts +8 -0
- package/dist/types/plugin.d.ts.map +1 -0
- package/dist/types/split.d.ts +7 -0
- package/dist/types/split.d.ts.map +1 -0
- package/dist/types/transform-block.d.ts +5 -0
- package/dist/types/transform-block.d.ts.map +1 -0
- package/dist/types/transform-call.d.ts +5 -0
- package/dist/types/transform-call.d.ts.map +1 -0
- package/dist/types/types.d.ts +62 -0
- package/dist/types/types.d.ts.map +1 -0
- package/dist/types/utils/assert.d.ts +2 -0
- package/dist/types/utils/assert.d.ts.map +1 -0
- package/dist/types/utils/generator-shim.d.ts +4 -0
- package/dist/types/utils/generator-shim.d.ts.map +1 -0
- package/dist/types/utils/get-descriptive-name.d.ts +3 -0
- package/dist/types/utils/get-descriptive-name.d.ts.map +1 -0
- package/dist/types/utils/get-foreign-bindings.d.ts +3 -0
- package/dist/types/utils/get-foreign-bindings.d.ts.map +1 -0
- package/dist/types/utils/get-identifiers-from-lval.d.ts +3 -0
- package/dist/types/utils/get-identifiers-from-lval.d.ts.map +1 -0
- package/dist/types/utils/get-import-identifier.d.ts +5 -0
- package/dist/types/utils/get-import-identifier.d.ts.map +1 -0
- package/dist/types/utils/get-import-specifier-name.d.ts +3 -0
- package/dist/types/utils/get-import-specifier-name.d.ts.map +1 -0
- package/dist/types/utils/get-module-definition.d.ts +4 -0
- package/dist/types/utils/get-module-definition.d.ts.map +1 -0
- package/dist/types/utils/get-root-statement-path.d.ts +3 -0
- package/dist/types/utils/get-root-statement-path.d.ts.map +1 -0
- package/dist/types/utils/register-import-specifiers.d.ts +5 -0
- package/dist/types/utils/register-import-specifiers.d.ts.map +1 -0
- package/dist/types/utils/unwrap.d.ts +11 -0
- package/dist/types/utils/unwrap.d.ts.map +1 -0
- package/dist/types/utils/xxhash32.d.ts +7 -0
- package/dist/types/utils/xxhash32.d.ts.map +1 -0
- package/package.json +90 -0
- package/src/hidden-imports.ts +13 -0
- package/src/index.ts +90 -0
- package/src/plugin.ts +26 -0
- package/src/split.ts +1074 -0
- package/src/transform-block.ts +115 -0
- package/src/transform-call.ts +100 -0
- package/src/types.ts +73 -0
- package/src/utils/assert.ts +5 -0
- package/src/utils/errors.ts +21 -0
- package/src/utils/generator-shim.ts +22 -0
- package/src/utils/get-descriptive-name.ts +42 -0
- package/src/utils/get-foreign-bindings.ts +60 -0
- package/src/utils/get-identifiers-from-lval.ts +44 -0
- package/src/utils/get-import-identifier.ts +23 -0
- package/src/utils/get-import-specifier-name.ts +10 -0
- package/src/utils/get-module-definition.ts +53 -0
- package/src/utils/get-root-statement-path.ts +14 -0
- package/src/utils/register-import-specifiers.ts +77 -0
- package/src/utils/unwrap.ts +63 -0
- package/src/utils/xxhash32.ts +214 -0
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import type * as babel from '@babel/core';
|
|
2
|
+
import type * as t from '@babel/types';
|
|
3
|
+
import { splitBlock } from './split';
|
|
4
|
+
import type { DirectiveDefinition, StateContext } from './types';
|
|
5
|
+
|
|
6
|
+
function getValidDirectiveFromString(
|
|
7
|
+
ctx: StateContext,
|
|
8
|
+
string: string,
|
|
9
|
+
): DirectiveDefinition | undefined {
|
|
10
|
+
for (let i = 0, len = ctx.options.directives.length; i < len; i++) {
|
|
11
|
+
const current = ctx.options.directives[i];
|
|
12
|
+
if (current.value === string) {
|
|
13
|
+
return current;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return undefined;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function getDefinitionFromDirectives(
|
|
20
|
+
ctx: StateContext,
|
|
21
|
+
path: babel.NodePath<t.BlockStatement>,
|
|
22
|
+
): DirectiveDefinition | undefined {
|
|
23
|
+
for (let i = 0, len = path.node.directives.length; i < len; i++) {
|
|
24
|
+
const statement = path.node.directives[i].value.value;
|
|
25
|
+
const directive = getValidDirectiveFromString(ctx, statement);
|
|
26
|
+
if (directive) {
|
|
27
|
+
return directive;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function getDefinitionFromFauxDirectives(
|
|
34
|
+
ctx: StateContext,
|
|
35
|
+
path: babel.NodePath<t.BlockStatement>,
|
|
36
|
+
): DirectiveDefinition | undefined {
|
|
37
|
+
for (let i = 0, len = path.node.body.length; i < len; i++) {
|
|
38
|
+
const statement = path.node.body[i];
|
|
39
|
+
if (
|
|
40
|
+
statement.type === 'ExpressionStatement' &&
|
|
41
|
+
statement.expression.type === 'StringLiteral'
|
|
42
|
+
) {
|
|
43
|
+
const directive = getValidDirectiveFromString(
|
|
44
|
+
ctx,
|
|
45
|
+
statement.expression.value,
|
|
46
|
+
);
|
|
47
|
+
if (directive) {
|
|
48
|
+
return directive;
|
|
49
|
+
}
|
|
50
|
+
} else {
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function getDirectiveDefinitionFromBlock(
|
|
58
|
+
ctx: StateContext,
|
|
59
|
+
path: babel.NodePath<t.BlockStatement>,
|
|
60
|
+
): DirectiveDefinition | undefined {
|
|
61
|
+
const parent = path.getFunctionParent();
|
|
62
|
+
if (parent && !parent.node.async) {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
return (
|
|
66
|
+
getDefinitionFromDirectives(ctx, path) ||
|
|
67
|
+
getDefinitionFromFauxDirectives(ctx, path)
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function cleanBlockForDirectives(
|
|
72
|
+
path: babel.NodePath<t.BlockStatement>,
|
|
73
|
+
definition: DirectiveDefinition,
|
|
74
|
+
): void {
|
|
75
|
+
const newDirectives: t.Directive[] = [];
|
|
76
|
+
for (let i = 0, len = path.node.directives.length; i < len; i++) {
|
|
77
|
+
const current = path.node.directives[i];
|
|
78
|
+
if (current.value.value !== definition.value) {
|
|
79
|
+
newDirectives.push(current);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
path.node.directives = newDirectives;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function cleanBlockForFauxDirectives(
|
|
86
|
+
path: babel.NodePath<t.BlockStatement>,
|
|
87
|
+
definition: DirectiveDefinition,
|
|
88
|
+
): void {
|
|
89
|
+
const body = path.get('body');
|
|
90
|
+
for (let i = 0, len = body.length; i < len; i++) {
|
|
91
|
+
const statement = body[i];
|
|
92
|
+
if (
|
|
93
|
+
statement.node.type === 'ExpressionStatement' &&
|
|
94
|
+
statement.node.expression.type === 'StringLiteral'
|
|
95
|
+
) {
|
|
96
|
+
if (statement.node.expression.value === definition.value) {
|
|
97
|
+
statement.remove();
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function transformBlock(
|
|
105
|
+
ctx: StateContext,
|
|
106
|
+
path: babel.NodePath<t.BlockStatement>,
|
|
107
|
+
): void {
|
|
108
|
+
const definition = getDirectiveDefinitionFromBlock(ctx, path);
|
|
109
|
+
if (!definition) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
cleanBlockForDirectives(path, definition);
|
|
113
|
+
cleanBlockForFauxDirectives(path, definition);
|
|
114
|
+
splitBlock(ctx, path, definition);
|
|
115
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type * as babel from '@babel/core';
|
|
2
|
+
import * as t from '@babel/types';
|
|
3
|
+
import { splitExpression, splitFunction } from './split';
|
|
4
|
+
import type { FunctionDefinition, StateContext } from './types';
|
|
5
|
+
import { isPathValid, unwrapNode } from './utils/unwrap';
|
|
6
|
+
|
|
7
|
+
function getFunctionDefinitionFromPropName(
|
|
8
|
+
definitions: FunctionDefinition[],
|
|
9
|
+
propName: string,
|
|
10
|
+
): FunctionDefinition | undefined {
|
|
11
|
+
for (let i = 0, len = definitions.length; i < len; i++) {
|
|
12
|
+
const def = definitions[i];
|
|
13
|
+
if (def.source.kind === 'default' && propName === 'default') {
|
|
14
|
+
return def;
|
|
15
|
+
}
|
|
16
|
+
if (def.source.kind === 'named' && propName === def.source.name) {
|
|
17
|
+
return def;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function getFunctionDefinitionFromCallee(
|
|
24
|
+
ctx: StateContext,
|
|
25
|
+
path: babel.NodePath<t.CallExpression>,
|
|
26
|
+
): FunctionDefinition | undefined {
|
|
27
|
+
const callee = path.node.callee;
|
|
28
|
+
const id = unwrapNode(callee, t.isIdentifier);
|
|
29
|
+
if (id) {
|
|
30
|
+
const binding = path.scope.getBindingIdentifier(id.name);
|
|
31
|
+
if (binding) {
|
|
32
|
+
return ctx.registrations.identifiers.get(binding);
|
|
33
|
+
}
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
const memberExpr = unwrapNode(callee, t.isMemberExpression);
|
|
37
|
+
if (
|
|
38
|
+
memberExpr &&
|
|
39
|
+
!memberExpr.computed &&
|
|
40
|
+
t.isIdentifier(memberExpr.property)
|
|
41
|
+
) {
|
|
42
|
+
const object = unwrapNode(memberExpr.object, t.isIdentifier);
|
|
43
|
+
if (object) {
|
|
44
|
+
const binding = path.scope.getBindingIdentifier(object.name);
|
|
45
|
+
if (binding) {
|
|
46
|
+
const definitions = ctx.registrations.namespaces.get(binding);
|
|
47
|
+
if (definitions) {
|
|
48
|
+
return getFunctionDefinitionFromPropName(
|
|
49
|
+
definitions,
|
|
50
|
+
memberExpr.property.name,
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function isValidFunction(
|
|
60
|
+
node: t.Node,
|
|
61
|
+
): node is t.ArrowFunctionExpression | t.FunctionExpression {
|
|
62
|
+
return t.isArrowFunctionExpression(node) || t.isFunctionExpression(node);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function isSkippableFunction(node: t.Expression): boolean {
|
|
66
|
+
if (node.leadingComments) {
|
|
67
|
+
for (let i = 0, len = node.leadingComments.length; i < len; i++) {
|
|
68
|
+
if (/^@dismantle skip$/.test(node.leadingComments[i].value)) {
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function transformCall(
|
|
77
|
+
ctx: StateContext,
|
|
78
|
+
path: babel.NodePath<t.CallExpression>,
|
|
79
|
+
): void {
|
|
80
|
+
const definition = getFunctionDefinitionFromCallee(ctx, path);
|
|
81
|
+
if (!definition) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
const args = path.get('arguments');
|
|
85
|
+
const expr = args[0];
|
|
86
|
+
if (isPathValid(expr, t.isExpression)) {
|
|
87
|
+
if (isSkippableFunction(expr.node)) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
const replacement = isPathValid(expr, isValidFunction)
|
|
91
|
+
? splitFunction(ctx, expr, definition)
|
|
92
|
+
: splitExpression(ctx, expr, definition);
|
|
93
|
+
|
|
94
|
+
if (definition.preserve) {
|
|
95
|
+
expr.replaceWith(t.addComment(replacement, 'leading', '@dismantle skip'));
|
|
96
|
+
} else {
|
|
97
|
+
path.replaceWith(replacement);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type * as babel from '@babel/core';
|
|
2
|
+
import type * as t from '@babel/types';
|
|
3
|
+
import type * as path from 'node:path';
|
|
4
|
+
|
|
5
|
+
export interface NamedImportDefinition {
|
|
6
|
+
kind: 'named';
|
|
7
|
+
name: string;
|
|
8
|
+
source: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface DefaultImportDefinition {
|
|
12
|
+
kind: 'default';
|
|
13
|
+
source: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export type ImportDefinition = DefaultImportDefinition | NamedImportDefinition;
|
|
17
|
+
|
|
18
|
+
export interface DirectiveDefinition {
|
|
19
|
+
value: string;
|
|
20
|
+
target: ImportDefinition;
|
|
21
|
+
pure?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface FunctionDefinition {
|
|
25
|
+
source: ImportDefinition;
|
|
26
|
+
target: ImportDefinition;
|
|
27
|
+
preserve?: boolean;
|
|
28
|
+
pure?: boolean;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface Options {
|
|
32
|
+
key: string;
|
|
33
|
+
mode: 'server' | 'client';
|
|
34
|
+
env: 'production' | 'development';
|
|
35
|
+
directives: DirectiveDefinition[];
|
|
36
|
+
functions: FunctionDefinition[];
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface ModuleDefinition {
|
|
40
|
+
source: string;
|
|
41
|
+
kind: 'default' | 'named' | 'namespace';
|
|
42
|
+
local: string;
|
|
43
|
+
imported?: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface CodeOutput {
|
|
47
|
+
code: babel.BabelFileResult['code'];
|
|
48
|
+
map: babel.BabelFileResult['map'];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface StateContext {
|
|
52
|
+
id: string;
|
|
53
|
+
path: path.ParsedPath;
|
|
54
|
+
imports: Map<string, t.Identifier>;
|
|
55
|
+
virtual: {
|
|
56
|
+
count: number;
|
|
57
|
+
};
|
|
58
|
+
blocks: {
|
|
59
|
+
hash: string;
|
|
60
|
+
count: number;
|
|
61
|
+
};
|
|
62
|
+
bindings: Map<string, ModuleDefinition>;
|
|
63
|
+
options: Options;
|
|
64
|
+
onVirtualFile: (
|
|
65
|
+
path: string,
|
|
66
|
+
content: CodeOutput,
|
|
67
|
+
mode: 'entry' | 'root' | 'none',
|
|
68
|
+
) => void;
|
|
69
|
+
registrations: {
|
|
70
|
+
identifiers: Map<t.Identifier, FunctionDefinition>;
|
|
71
|
+
namespaces: Map<t.Identifier, FunctionDefinition[]>;
|
|
72
|
+
};
|
|
73
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type * as babel from '@babel/core';
|
|
2
|
+
|
|
3
|
+
export function unexpectedType<T>(
|
|
4
|
+
path: babel.NodePath<T>,
|
|
5
|
+
received: string,
|
|
6
|
+
expected: string,
|
|
7
|
+
): Error {
|
|
8
|
+
return path.buildCodeFrameError(
|
|
9
|
+
`Unexpected '${received}' (Expected: ${expected})`,
|
|
10
|
+
);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function unexpectedArgumentLength<T>(
|
|
14
|
+
path: babel.NodePath<T>,
|
|
15
|
+
received: number,
|
|
16
|
+
expected: number,
|
|
17
|
+
): Error {
|
|
18
|
+
return path.buildCodeFrameError(
|
|
19
|
+
`Unexpected argument length of ${received} (Expected: at least ${expected})`,
|
|
20
|
+
);
|
|
21
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import _generator from '@babel/generator';
|
|
2
|
+
import type * as t from '@babel/types';
|
|
3
|
+
import type { CodeOutput } from '../types';
|
|
4
|
+
|
|
5
|
+
type GeneratorShim = typeof _generator;
|
|
6
|
+
|
|
7
|
+
// https://github.com/babel/babel/issues/15269
|
|
8
|
+
const generator: GeneratorShim =
|
|
9
|
+
typeof _generator !== 'function'
|
|
10
|
+
? (_generator as unknown as { default: GeneratorShim }).default
|
|
11
|
+
: _generator;
|
|
12
|
+
|
|
13
|
+
export function generateCode(id: string, node: t.Node): CodeOutput {
|
|
14
|
+
const result = generator(node, {
|
|
15
|
+
sourceMaps: true,
|
|
16
|
+
sourceFileName: id,
|
|
17
|
+
});
|
|
18
|
+
return {
|
|
19
|
+
code: result.code,
|
|
20
|
+
map: result.map,
|
|
21
|
+
};
|
|
22
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { NodePath } from '@babel/core';
|
|
2
|
+
|
|
3
|
+
export function getDescriptiveName(
|
|
4
|
+
path: NodePath,
|
|
5
|
+
defaultName: string,
|
|
6
|
+
): string {
|
|
7
|
+
let current: NodePath | null = path;
|
|
8
|
+
while (current) {
|
|
9
|
+
switch (current.node.type) {
|
|
10
|
+
case 'FunctionDeclaration':
|
|
11
|
+
case 'FunctionExpression': {
|
|
12
|
+
if (current.node.id) {
|
|
13
|
+
return current.node.id.name;
|
|
14
|
+
}
|
|
15
|
+
break;
|
|
16
|
+
}
|
|
17
|
+
case 'VariableDeclarator': {
|
|
18
|
+
if (current.node.id.type === 'Identifier') {
|
|
19
|
+
return current.node.id.name;
|
|
20
|
+
}
|
|
21
|
+
break;
|
|
22
|
+
}
|
|
23
|
+
case 'ClassPrivateMethod':
|
|
24
|
+
case 'ClassMethod':
|
|
25
|
+
case 'ObjectMethod': {
|
|
26
|
+
switch (current.node.key.type) {
|
|
27
|
+
case 'Identifier':
|
|
28
|
+
return current.node.key.name;
|
|
29
|
+
case 'PrivateName':
|
|
30
|
+
return current.node.key.id.name;
|
|
31
|
+
default:
|
|
32
|
+
break;
|
|
33
|
+
}
|
|
34
|
+
break;
|
|
35
|
+
}
|
|
36
|
+
default:
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
current = current.parentPath;
|
|
40
|
+
}
|
|
41
|
+
return defaultName;
|
|
42
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type * as babel from '@babel/core';
|
|
2
|
+
import * as t from '@babel/types';
|
|
3
|
+
|
|
4
|
+
function isForeignBinding(
|
|
5
|
+
source: babel.NodePath,
|
|
6
|
+
current: babel.NodePath,
|
|
7
|
+
name: string,
|
|
8
|
+
mode: 'block' | 'function',
|
|
9
|
+
): boolean {
|
|
10
|
+
if (current.scope.hasGlobal(name)) {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
if (source === current) {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
if (current.scope.hasOwnBinding(name)) {
|
|
17
|
+
if (mode === 'block') {
|
|
18
|
+
const binding = current.scope.getBinding(name);
|
|
19
|
+
return !!binding && binding.kind === 'param';
|
|
20
|
+
}
|
|
21
|
+
return false;
|
|
22
|
+
}
|
|
23
|
+
if (current.parentPath) {
|
|
24
|
+
return isForeignBinding(source, current.parentPath, name, mode);
|
|
25
|
+
}
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function isInTypescript(path: babel.NodePath): boolean {
|
|
30
|
+
let parent = path.parentPath;
|
|
31
|
+
while (parent) {
|
|
32
|
+
if (t.isTypeScript(parent.node) && !t.isExpression(parent.node)) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
parent = parent.parentPath;
|
|
36
|
+
}
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export default function getForeignBindings(
|
|
41
|
+
path: babel.NodePath,
|
|
42
|
+
mode: 'block' | 'function' | 'expression',
|
|
43
|
+
): Set<string> {
|
|
44
|
+
const identifiers = new Set<string>();
|
|
45
|
+
path.traverse({
|
|
46
|
+
ReferencedIdentifier(p) {
|
|
47
|
+
// Check identifiers that aren't in a TS expression
|
|
48
|
+
if (
|
|
49
|
+
!isInTypescript(p) &&
|
|
50
|
+
(mode === 'expression'
|
|
51
|
+
? !path.scope.hasGlobal(p.node.name)
|
|
52
|
+
: isForeignBinding(path, p, p.node.name, mode))
|
|
53
|
+
) {
|
|
54
|
+
identifiers.add(p.node.name);
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
return identifiers;
|
|
60
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import * as t from '@babel/types';
|
|
2
|
+
|
|
3
|
+
function getIdentifiersFromArrayPattern(node: t.ArrayPattern): string[] {
|
|
4
|
+
const ids: string[] = [];
|
|
5
|
+
for (let i = 0, len = node.elements.length; i < len; i++) {
|
|
6
|
+
const el = node.elements[i];
|
|
7
|
+
if (el) {
|
|
8
|
+
ids.push(...getIdentifiersFromLVal(el));
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
return ids;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function getIdentifiersFromObjectPattern(node: t.ObjectPattern): string[] {
|
|
15
|
+
const ids: string[] = [];
|
|
16
|
+
for (let i = 0, len = node.properties.length; i < len; i++) {
|
|
17
|
+
const el = node.properties[i];
|
|
18
|
+
if (el) {
|
|
19
|
+
if (el.type === 'RestElement') {
|
|
20
|
+
ids.push(...getIdentifiersFromLVal(el));
|
|
21
|
+
} else if (t.isLVal(el.value)) {
|
|
22
|
+
ids.push(...getIdentifiersFromLVal(el.value));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
return ids;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function getIdentifiersFromLVal(node: t.LVal): string[] {
|
|
30
|
+
switch (node.type) {
|
|
31
|
+
case 'Identifier':
|
|
32
|
+
return [node.name];
|
|
33
|
+
case 'ArrayPattern':
|
|
34
|
+
return getIdentifiersFromArrayPattern(node);
|
|
35
|
+
case 'AssignmentPattern':
|
|
36
|
+
return getIdentifiersFromLVal(node.left);
|
|
37
|
+
case 'ObjectPattern':
|
|
38
|
+
return getIdentifiersFromObjectPattern(node);
|
|
39
|
+
case 'RestElement':
|
|
40
|
+
return getIdentifiersFromLVal(node.argument);
|
|
41
|
+
default:
|
|
42
|
+
return [];
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type * as babel from '@babel/core';
|
|
2
|
+
import { addDefault, addNamed } from '@babel/helper-module-imports';
|
|
3
|
+
import type * as t from '@babel/types';
|
|
4
|
+
import type { ImportDefinition, StateContext } from '../types';
|
|
5
|
+
|
|
6
|
+
export function getImportIdentifier(
|
|
7
|
+
state: StateContext,
|
|
8
|
+
path: babel.NodePath,
|
|
9
|
+
registration: ImportDefinition,
|
|
10
|
+
): t.Identifier {
|
|
11
|
+
const name = registration.kind === 'named' ? registration.name : 'default';
|
|
12
|
+
const target = `${registration.source}[${name}]`;
|
|
13
|
+
const current = state.imports.get(target);
|
|
14
|
+
if (current) {
|
|
15
|
+
return current;
|
|
16
|
+
}
|
|
17
|
+
const newID =
|
|
18
|
+
registration.kind === 'named'
|
|
19
|
+
? addNamed(path, registration.name, registration.source)
|
|
20
|
+
: addDefault(path, registration.source);
|
|
21
|
+
state.imports.set(target, newID);
|
|
22
|
+
return newID;
|
|
23
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type * as babel from '@babel/core';
|
|
2
|
+
import * as t from '@babel/types';
|
|
3
|
+
import type { ModuleDefinition } from '../types';
|
|
4
|
+
import { getImportSpecifierName } from './get-import-specifier-name';
|
|
5
|
+
import { isPathValid } from './unwrap';
|
|
6
|
+
|
|
7
|
+
export function getModuleDefinition(
|
|
8
|
+
path: babel.NodePath,
|
|
9
|
+
): ModuleDefinition | undefined {
|
|
10
|
+
if (
|
|
11
|
+
!(
|
|
12
|
+
isPathValid(path, t.isImportSpecifier) ||
|
|
13
|
+
isPathValid(path, t.isImportDefaultSpecifier) ||
|
|
14
|
+
isPathValid(path, t.isImportNamespaceSpecifier)
|
|
15
|
+
)
|
|
16
|
+
) {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
const parent =
|
|
20
|
+
path.getStatementParent() as babel.NodePath<t.ImportDeclaration>;
|
|
21
|
+
const source = parent.node.source.value;
|
|
22
|
+
switch (path.node.type) {
|
|
23
|
+
case 'ImportDefaultSpecifier':
|
|
24
|
+
return {
|
|
25
|
+
source,
|
|
26
|
+
kind: 'default',
|
|
27
|
+
local: path.node.local.name,
|
|
28
|
+
};
|
|
29
|
+
case 'ImportNamespaceSpecifier':
|
|
30
|
+
return {
|
|
31
|
+
source,
|
|
32
|
+
kind: 'namespace',
|
|
33
|
+
local: path.node.local.name,
|
|
34
|
+
};
|
|
35
|
+
case 'ImportSpecifier': {
|
|
36
|
+
const imported = getImportSpecifierName(path.node);
|
|
37
|
+
if (imported === 'default') {
|
|
38
|
+
return {
|
|
39
|
+
source,
|
|
40
|
+
kind: 'default',
|
|
41
|
+
local: path.node.local.name,
|
|
42
|
+
imported: '',
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
source,
|
|
47
|
+
kind: 'named',
|
|
48
|
+
local: path.node.local.name,
|
|
49
|
+
imported,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type * as babel from '@babel/core';
|
|
2
|
+
import * as t from '@babel/types';
|
|
3
|
+
|
|
4
|
+
export function getRootStatementPath(path: babel.NodePath): babel.NodePath {
|
|
5
|
+
let current = path.parentPath;
|
|
6
|
+
while (current) {
|
|
7
|
+
const next = current.parentPath;
|
|
8
|
+
if (next && t.isProgram(next.node)) {
|
|
9
|
+
return current;
|
|
10
|
+
}
|
|
11
|
+
current = next;
|
|
12
|
+
}
|
|
13
|
+
return path;
|
|
14
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type * as babel from '@babel/core';
|
|
2
|
+
import * as t from '@babel/types';
|
|
3
|
+
import type { FunctionDefinition, StateContext } from '../types';
|
|
4
|
+
import { getImportSpecifierName } from './get-import-specifier-name';
|
|
5
|
+
|
|
6
|
+
function registerImportSpecifier(
|
|
7
|
+
ctx: StateContext,
|
|
8
|
+
node:
|
|
9
|
+
| t.ImportSpecifier
|
|
10
|
+
| t.ImportDefaultSpecifier
|
|
11
|
+
| t.ImportNamespaceSpecifier,
|
|
12
|
+
definition: FunctionDefinition,
|
|
13
|
+
): void {
|
|
14
|
+
if (t.isImportSpecifier(node)) {
|
|
15
|
+
if (node.importKind === 'type' || node.importKind === 'typeof') {
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
const key = getImportSpecifierName(node);
|
|
19
|
+
if (
|
|
20
|
+
(definition.source.kind === 'named' && key === definition.source.name) ||
|
|
21
|
+
(definition.source.kind === 'default' && key === 'default')
|
|
22
|
+
) {
|
|
23
|
+
ctx.registrations.identifiers.set(node.local, definition);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
if (
|
|
27
|
+
t.isImportDefaultSpecifier(node) &&
|
|
28
|
+
definition.source.kind === 'default'
|
|
29
|
+
) {
|
|
30
|
+
ctx.registrations.identifiers.set(node.local, definition);
|
|
31
|
+
}
|
|
32
|
+
if (t.isImportNamespaceSpecifier(node)) {
|
|
33
|
+
let current = ctx.registrations.namespaces.get(node.local);
|
|
34
|
+
if (!current) {
|
|
35
|
+
current = [];
|
|
36
|
+
}
|
|
37
|
+
current.push(definition);
|
|
38
|
+
ctx.registrations.namespaces.set(node.local, current);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function registerImportDeclarationByDefinition(
|
|
43
|
+
ctx: StateContext,
|
|
44
|
+
path: babel.NodePath<t.ImportDeclaration>,
|
|
45
|
+
definition: FunctionDefinition,
|
|
46
|
+
): void {
|
|
47
|
+
for (let i = 0, len = path.node.specifiers.length; i < len; i++) {
|
|
48
|
+
const specifier = path.node.specifiers[i];
|
|
49
|
+
registerImportSpecifier(ctx, specifier, definition);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function registerImportSpecifiers(
|
|
54
|
+
ctx: StateContext,
|
|
55
|
+
programPath: babel.NodePath<t.Program>,
|
|
56
|
+
): void {
|
|
57
|
+
const len = ctx.options.functions.length;
|
|
58
|
+
if (!len) {
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
programPath.traverse({
|
|
62
|
+
ImportDeclaration(path) {
|
|
63
|
+
if (
|
|
64
|
+
path.node.importKind === 'type' ||
|
|
65
|
+
path.node.importKind === 'typeof'
|
|
66
|
+
) {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
for (let i = 0; i < len; i++) {
|
|
70
|
+
const func = ctx.options.functions[i];
|
|
71
|
+
if (func.source.source === path.node.source.value) {
|
|
72
|
+
registerImportDeclarationByDefinition(ctx, path, func);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
}
|