@test328932/test328933 1.0.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/package.json +41 -0
- package/src/component-border.js +839 -0
- package/src/component-values.js +248 -0
- package/src/index.d.ts +10 -0
- package/src/index.js +6 -0
- package/src/llm-explain-inject.js +218 -0
- package/src/runtime/index.d.ts +4 -0
- package/src/runtime/index.js +2 -0
- package/src/runtime/llm-explain-panel.jsx +813 -0
- package/src/runtime/llm-worker.js +7 -0
- package/src/save-file.js +46 -0
- package/src/source-compressor.js +352 -0
- package/src/source-explorer.js +129 -0
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
import { parse } from '@babel/parser'
|
|
2
|
+
import traverse from '@babel/traverse'
|
|
3
|
+
import generate from '@babel/generator'
|
|
4
|
+
import * as t from '@babel/types'
|
|
5
|
+
|
|
6
|
+
const traverseAst = traverse.default ?? traverse
|
|
7
|
+
const generateCode = generate.default ?? generate
|
|
8
|
+
|
|
9
|
+
function isComponentName(name) {
|
|
10
|
+
return typeof name === 'string' && /^[A-Z]/.test(name)
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function isTopLevelDeclaration(path) {
|
|
14
|
+
const parent = path.parentPath
|
|
15
|
+
if (!parent) return false
|
|
16
|
+
if (parent.isProgram()) return true
|
|
17
|
+
if (parent.isExportNamedDeclaration() || parent.isExportDefaultDeclaration()) {
|
|
18
|
+
return parent.parentPath?.isProgram() ?? false
|
|
19
|
+
}
|
|
20
|
+
return false
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function ensureBlockBody(functionNode) {
|
|
24
|
+
if (t.isBlockStatement(functionNode.body)) return
|
|
25
|
+
functionNode.body = t.blockStatement([t.returnStatement(functionNode.body)])
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function isUseStateCall(callee) {
|
|
29
|
+
if (t.isIdentifier(callee, { name: 'useState' })) return true
|
|
30
|
+
if (t.isMemberExpression(callee) && t.isIdentifier(callee.property, { name: 'useState' })) {
|
|
31
|
+
return true
|
|
32
|
+
}
|
|
33
|
+
return false
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function collectPatternIdentifiers(pattern, output) {
|
|
37
|
+
if (!pattern) return
|
|
38
|
+
|
|
39
|
+
if (t.isIdentifier(pattern)) {
|
|
40
|
+
output.push(pattern.name)
|
|
41
|
+
return
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (t.isAssignmentPattern(pattern)) {
|
|
45
|
+
collectPatternIdentifiers(pattern.left, output)
|
|
46
|
+
return
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (t.isRestElement(pattern)) {
|
|
50
|
+
collectPatternIdentifiers(pattern.argument, output)
|
|
51
|
+
return
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (t.isObjectPattern(pattern)) {
|
|
55
|
+
pattern.properties.forEach(prop => {
|
|
56
|
+
if (t.isRestElement(prop)) {
|
|
57
|
+
collectPatternIdentifiers(prop.argument, output)
|
|
58
|
+
return
|
|
59
|
+
}
|
|
60
|
+
if (t.isObjectProperty(prop)) {
|
|
61
|
+
collectPatternIdentifiers(prop.value, output)
|
|
62
|
+
}
|
|
63
|
+
})
|
|
64
|
+
return
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (t.isArrayPattern(pattern)) {
|
|
68
|
+
pattern.elements.forEach(element => {
|
|
69
|
+
collectPatternIdentifiers(element, output)
|
|
70
|
+
})
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function buildPropsExpression(paramNode) {
|
|
75
|
+
if (!paramNode) return t.nullLiteral()
|
|
76
|
+
|
|
77
|
+
if (t.isIdentifier(paramNode)) {
|
|
78
|
+
return t.identifier(paramNode.name)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const names = []
|
|
82
|
+
collectPatternIdentifiers(paramNode, names)
|
|
83
|
+
const uniqueNames = [...new Set(names)]
|
|
84
|
+
|
|
85
|
+
return t.objectExpression(
|
|
86
|
+
uniqueNames.map(name => t.objectProperty(t.identifier(name), t.identifier(name), false, true)),
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function collectStateNames(functionPath) {
|
|
91
|
+
const stateNames = []
|
|
92
|
+
|
|
93
|
+
functionPath.get('body').traverse({
|
|
94
|
+
VariableDeclarator(variablePath) {
|
|
95
|
+
if (variablePath.getFunctionParent() !== functionPath) return
|
|
96
|
+
|
|
97
|
+
const { id, init } = variablePath.node
|
|
98
|
+
if (!t.isArrayPattern(id) || !t.isCallExpression(init)) return
|
|
99
|
+
if (!isUseStateCall(init.callee)) return
|
|
100
|
+
|
|
101
|
+
const firstElement = id.elements[0]
|
|
102
|
+
if (t.isIdentifier(firstElement)) {
|
|
103
|
+
stateNames.push(firstElement.name)
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
})
|
|
107
|
+
|
|
108
|
+
return [...new Set(stateNames)]
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function buildInspectorTextarea(snapshotExpression) {
|
|
112
|
+
const textareaStyle = t.objectExpression([
|
|
113
|
+
t.objectProperty(t.identifier('width'), t.stringLiteral('100%')),
|
|
114
|
+
t.objectProperty(t.identifier('minHeight'), t.stringLiteral('130px')),
|
|
115
|
+
t.objectProperty(t.identifier('marginTop'), t.stringLiteral('10px')),
|
|
116
|
+
t.objectProperty(t.identifier('padding'), t.stringLiteral('8px')),
|
|
117
|
+
t.objectProperty(t.identifier('fontFamily'), t.stringLiteral('ui-monospace, SFMono-Regular, Menlo, monospace')),
|
|
118
|
+
t.objectProperty(t.identifier('fontSize'), t.stringLiteral('12px')),
|
|
119
|
+
t.objectProperty(t.identifier('whiteSpace'), t.stringLiteral('pre')),
|
|
120
|
+
t.objectProperty(t.identifier('background'), t.stringLiteral('#fbfbff')),
|
|
121
|
+
t.objectProperty(t.identifier('border'), t.stringLiteral('1px solid #d3d8eb')),
|
|
122
|
+
t.objectProperty(t.identifier('borderRadius'), t.stringLiteral('6px')),
|
|
123
|
+
])
|
|
124
|
+
|
|
125
|
+
return t.jsxElement(
|
|
126
|
+
t.jsxOpeningElement(
|
|
127
|
+
t.jsxIdentifier('textarea'),
|
|
128
|
+
[
|
|
129
|
+
t.jsxAttribute(t.jsxIdentifier('readOnly'), null),
|
|
130
|
+
t.jsxAttribute(
|
|
131
|
+
t.jsxIdentifier('value'),
|
|
132
|
+
t.jsxExpressionContainer(
|
|
133
|
+
t.callExpression(
|
|
134
|
+
t.memberExpression(t.identifier('JSON'), t.identifier('stringify')),
|
|
135
|
+
[t.cloneNode(snapshotExpression), t.nullLiteral(), t.numericLiteral(2)],
|
|
136
|
+
),
|
|
137
|
+
),
|
|
138
|
+
),
|
|
139
|
+
t.jsxAttribute(
|
|
140
|
+
t.jsxIdentifier('style'),
|
|
141
|
+
t.jsxExpressionContainer(textareaStyle),
|
|
142
|
+
),
|
|
143
|
+
],
|
|
144
|
+
false,
|
|
145
|
+
),
|
|
146
|
+
t.jsxClosingElement(t.jsxIdentifier('textarea')),
|
|
147
|
+
[],
|
|
148
|
+
false,
|
|
149
|
+
)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function wrapWithInspector(jsxNode, snapshotExpression) {
|
|
153
|
+
return t.jsxFragment(
|
|
154
|
+
t.jsxOpeningFragment(),
|
|
155
|
+
t.jsxClosingFragment(),
|
|
156
|
+
[buildInspectorTextarea(snapshotExpression), jsxNode],
|
|
157
|
+
)
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function transformComponentFunction(functionPath) {
|
|
161
|
+
ensureBlockBody(functionPath.node)
|
|
162
|
+
|
|
163
|
+
const returnPaths = []
|
|
164
|
+
functionPath.get('body').traverse({
|
|
165
|
+
ReturnStatement(returnPath) {
|
|
166
|
+
if (returnPath.getFunctionParent() !== functionPath) return
|
|
167
|
+
const arg = returnPath.node.argument
|
|
168
|
+
if (t.isJSXElement(arg) || t.isJSXFragment(arg)) {
|
|
169
|
+
returnPaths.push(returnPath)
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
if (returnPaths.length === 0) return false
|
|
175
|
+
|
|
176
|
+
const stateNames = collectStateNames(functionPath)
|
|
177
|
+
const propsExpression = buildPropsExpression(functionPath.node.params[0])
|
|
178
|
+
|
|
179
|
+
const stateExpression = t.objectExpression(
|
|
180
|
+
stateNames.map(name => t.objectProperty(t.identifier(name), t.identifier(name), false, true)),
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
const snapshotObject = t.objectExpression([
|
|
184
|
+
t.objectProperty(t.identifier('props'), propsExpression),
|
|
185
|
+
t.objectProperty(t.identifier('state'), stateExpression),
|
|
186
|
+
])
|
|
187
|
+
|
|
188
|
+
returnPaths.forEach(returnPath => {
|
|
189
|
+
const arg = returnPath.node.argument
|
|
190
|
+
if (!arg) return
|
|
191
|
+
returnPath.node.argument = wrapWithInspector(arg, snapshotObject)
|
|
192
|
+
})
|
|
193
|
+
|
|
194
|
+
return true
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export default function componentValuesPlugin() {
|
|
198
|
+
return {
|
|
199
|
+
name: 'vite-plugin-component-values',
|
|
200
|
+
enforce: 'pre',
|
|
201
|
+
transform(code, id) {
|
|
202
|
+
if (!/\.[jt]sx?$/.test(id) || id.includes('node_modules')) return
|
|
203
|
+
if (!code.includes('jsx') && !/<[A-Z]/.test(code) && !/<[a-z]/.test(code)) return
|
|
204
|
+
|
|
205
|
+
let ast
|
|
206
|
+
try {
|
|
207
|
+
ast = parse(code, {
|
|
208
|
+
sourceType: 'module',
|
|
209
|
+
plugins: ['jsx', 'typescript'],
|
|
210
|
+
})
|
|
211
|
+
} catch {
|
|
212
|
+
return
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
let modified = false
|
|
216
|
+
|
|
217
|
+
traverseAst(ast, {
|
|
218
|
+
VariableDeclarator(path) {
|
|
219
|
+
if (!t.isIdentifier(path.node.id) || !isComponentName(path.node.id.name)) return
|
|
220
|
+
if (!isTopLevelDeclaration(path.parentPath)) return
|
|
221
|
+
|
|
222
|
+
const { init } = path.node
|
|
223
|
+
if (!init) return
|
|
224
|
+
if (!t.isArrowFunctionExpression(init) && !t.isFunctionExpression(init)) return
|
|
225
|
+
|
|
226
|
+
const initPath = path.get('init')
|
|
227
|
+
if (transformComponentFunction(initPath)) {
|
|
228
|
+
modified = true
|
|
229
|
+
}
|
|
230
|
+
},
|
|
231
|
+
|
|
232
|
+
FunctionDeclaration(path) {
|
|
233
|
+
if (!path.node.id || !isComponentName(path.node.id.name)) return
|
|
234
|
+
if (!isTopLevelDeclaration(path)) return
|
|
235
|
+
|
|
236
|
+
if (transformComponentFunction(path)) {
|
|
237
|
+
modified = true
|
|
238
|
+
}
|
|
239
|
+
},
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
if (!modified) return
|
|
243
|
+
|
|
244
|
+
const output = generateCode(ast, { retainLines: true }, code)
|
|
245
|
+
return { code: output.code, map: output.map }
|
|
246
|
+
},
|
|
247
|
+
}
|
|
248
|
+
}
|
package/src/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export function componentBorder(): any;
|
|
2
|
+
export function componentValues(): any;
|
|
3
|
+
export function saveFile(): any;
|
|
4
|
+
export function sourceExplorer(): any;
|
|
5
|
+
export function llmExplainInject(options?: {
|
|
6
|
+
targetFile?: string;
|
|
7
|
+
workspaceFunction?: string;
|
|
8
|
+
sourcePath?: string;
|
|
9
|
+
}): any;
|
|
10
|
+
export function sourceCompressor(): any;
|
package/src/index.js
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export { default as componentBorder } from './component-border.js'
|
|
2
|
+
export { default as componentValues } from './component-values.js'
|
|
3
|
+
export { default as saveFile } from './save-file.js'
|
|
4
|
+
export { default as sourceExplorer } from './source-explorer.js'
|
|
5
|
+
export { default as llmExplainInject } from './llm-explain-inject.js'
|
|
6
|
+
export { default as sourceCompressor } from './source-compressor.js'
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import path from 'node:path'
|
|
2
|
+
import { parse } from '@babel/parser'
|
|
3
|
+
import traverse from '@babel/traverse'
|
|
4
|
+
import generate from '@babel/generator'
|
|
5
|
+
import * as t from '@babel/types'
|
|
6
|
+
|
|
7
|
+
const traverseAst = traverse.default ?? traverse
|
|
8
|
+
const generateCode = generate.default ?? generate
|
|
9
|
+
|
|
10
|
+
const RUNTIME_IMPORT_SOURCE = '@test328932/test328933/runtime'
|
|
11
|
+
const PANEL_COMPONENT_NAME = 'LlmExplainPanel'
|
|
12
|
+
|
|
13
|
+
function ensureBlockBody(functionNode) {
|
|
14
|
+
if (t.isBlockStatement(functionNode.body)) return
|
|
15
|
+
functionNode.body = t.blockStatement([t.returnStatement(functionNode.body)])
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function buildPanelElement(sourcePath) {
|
|
19
|
+
return t.jsxElement(
|
|
20
|
+
t.jsxOpeningElement(
|
|
21
|
+
t.jsxIdentifier(PANEL_COMPONENT_NAME),
|
|
22
|
+
[
|
|
23
|
+
t.jsxAttribute(
|
|
24
|
+
t.jsxIdentifier('sourcePath'),
|
|
25
|
+
t.stringLiteral(sourcePath),
|
|
26
|
+
),
|
|
27
|
+
],
|
|
28
|
+
true,
|
|
29
|
+
),
|
|
30
|
+
null,
|
|
31
|
+
[],
|
|
32
|
+
true,
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function hasPanelInJSX(node) {
|
|
37
|
+
let found = false
|
|
38
|
+
|
|
39
|
+
traverseAst(
|
|
40
|
+
t.file(t.program([t.expressionStatement(node)])),
|
|
41
|
+
{
|
|
42
|
+
JSXOpeningElement(path) {
|
|
43
|
+
if (t.isJSXIdentifier(path.node.name, { name: PANEL_COMPONENT_NAME })) {
|
|
44
|
+
found = true
|
|
45
|
+
path.stop()
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
return found
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function ensureRuntimeImport(ast) {
|
|
55
|
+
const body = ast.program.body
|
|
56
|
+
let runtimeImport = null
|
|
57
|
+
|
|
58
|
+
for (const statement of body) {
|
|
59
|
+
if (t.isImportDeclaration(statement) && statement.source.value === RUNTIME_IMPORT_SOURCE) {
|
|
60
|
+
runtimeImport = statement
|
|
61
|
+
break
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (runtimeImport) {
|
|
66
|
+
const hasSpecifier = runtimeImport.specifiers.some(specifier => (
|
|
67
|
+
t.isImportSpecifier(specifier) &&
|
|
68
|
+
t.isIdentifier(specifier.imported, { name: PANEL_COMPONENT_NAME })
|
|
69
|
+
))
|
|
70
|
+
|
|
71
|
+
if (!hasSpecifier) {
|
|
72
|
+
runtimeImport.specifiers.push(
|
|
73
|
+
t.importSpecifier(
|
|
74
|
+
t.identifier(PANEL_COMPONENT_NAME),
|
|
75
|
+
t.identifier(PANEL_COMPONENT_NAME),
|
|
76
|
+
),
|
|
77
|
+
)
|
|
78
|
+
return true
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return false
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const lastImportIndex = [...body].reverse().findIndex(statement => t.isImportDeclaration(statement))
|
|
85
|
+
const insertIndex = lastImportIndex === -1 ? 0 : body.length - lastImportIndex
|
|
86
|
+
|
|
87
|
+
body.splice(
|
|
88
|
+
insertIndex,
|
|
89
|
+
0,
|
|
90
|
+
t.importDeclaration(
|
|
91
|
+
[
|
|
92
|
+
t.importSpecifier(
|
|
93
|
+
t.identifier(PANEL_COMPONENT_NAME),
|
|
94
|
+
t.identifier(PANEL_COMPONENT_NAME),
|
|
95
|
+
),
|
|
96
|
+
],
|
|
97
|
+
t.stringLiteral(RUNTIME_IMPORT_SOURCE),
|
|
98
|
+
),
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
return true
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function findWorkspaceFunctionPath(ast, workspaceFunction) {
|
|
105
|
+
let workspacePath = null
|
|
106
|
+
|
|
107
|
+
traverseAst(ast, {
|
|
108
|
+
FunctionDeclaration(path) {
|
|
109
|
+
if (workspacePath) return
|
|
110
|
+
if (!path.node.id || path.node.id.name !== workspaceFunction) return
|
|
111
|
+
workspacePath = path
|
|
112
|
+
},
|
|
113
|
+
VariableDeclarator(path) {
|
|
114
|
+
if (workspacePath) return
|
|
115
|
+
if (!t.isIdentifier(path.node.id, { name: workspaceFunction })) return
|
|
116
|
+
const { init } = path.node
|
|
117
|
+
if (!init) return
|
|
118
|
+
if (!t.isFunctionExpression(init) && !t.isArrowFunctionExpression(init)) return
|
|
119
|
+
workspacePath = path.get('init')
|
|
120
|
+
},
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
return workspacePath
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function injectPanelIntoWorkspace(workspacePath, sourcePath) {
|
|
127
|
+
ensureBlockBody(workspacePath.node)
|
|
128
|
+
|
|
129
|
+
let returnPath = null
|
|
130
|
+
|
|
131
|
+
workspacePath.get('body').traverse({
|
|
132
|
+
ReturnStatement(path) {
|
|
133
|
+
if (path.getFunctionParent() !== workspacePath) return
|
|
134
|
+
if (returnPath) return
|
|
135
|
+
|
|
136
|
+
const arg = path.node.argument
|
|
137
|
+
if (t.isJSXElement(arg) || t.isJSXFragment(arg)) {
|
|
138
|
+
returnPath = path
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
if (!returnPath) return false
|
|
144
|
+
|
|
145
|
+
const returnArg = returnPath.node.argument
|
|
146
|
+
if (!returnArg) return false
|
|
147
|
+
|
|
148
|
+
if (hasPanelInJSX(returnArg)) return false
|
|
149
|
+
|
|
150
|
+
if (t.isJSXElement(returnArg) || t.isJSXFragment(returnArg)) {
|
|
151
|
+
returnArg.children.push(buildPanelElement(sourcePath))
|
|
152
|
+
return true
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return false
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export default function llmExplainInject(options = {}) {
|
|
159
|
+
const {
|
|
160
|
+
targetFile = 'src/App.tsx',
|
|
161
|
+
workspaceFunction = 'TodoWorkspace',
|
|
162
|
+
sourcePath = 'App.tsx',
|
|
163
|
+
} = options
|
|
164
|
+
|
|
165
|
+
let rootDir = process.cwd()
|
|
166
|
+
let targetAbsolutePath = path.resolve(rootDir, targetFile)
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
name: 'vite-plugin-llm-explain-inject',
|
|
170
|
+
enforce: 'pre',
|
|
171
|
+
configResolved(config) {
|
|
172
|
+
rootDir = config.root
|
|
173
|
+
targetAbsolutePath = path.resolve(rootDir, targetFile)
|
|
174
|
+
},
|
|
175
|
+
transform(code, id) {
|
|
176
|
+
const cleanId = id.split('?')[0]
|
|
177
|
+
if (path.resolve(cleanId) !== targetAbsolutePath) return
|
|
178
|
+
if (!/\.[jt]sx?$/.test(cleanId)) return
|
|
179
|
+
|
|
180
|
+
let ast
|
|
181
|
+
try {
|
|
182
|
+
ast = parse(code, {
|
|
183
|
+
sourceType: 'module',
|
|
184
|
+
plugins: ['jsx', 'typescript'],
|
|
185
|
+
})
|
|
186
|
+
} catch {
|
|
187
|
+
return
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const workspacePath = findWorkspaceFunctionPath(ast, workspaceFunction)
|
|
191
|
+
if (!workspacePath) {
|
|
192
|
+
this.warn(
|
|
193
|
+
`[vite-plugin-llm-explain-inject] Could not find workspace function "${workspaceFunction}" in ${targetFile}. No injection performed.`,
|
|
194
|
+
)
|
|
195
|
+
return
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
let modified = false
|
|
199
|
+
const panelInjected = injectPanelIntoWorkspace(workspacePath, sourcePath)
|
|
200
|
+
if (panelInjected) {
|
|
201
|
+
modified = true
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
const importModified = ensureRuntimeImport(ast)
|
|
205
|
+
if (importModified) {
|
|
206
|
+
modified = true
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (!modified) return
|
|
210
|
+
|
|
211
|
+
const output = generateCode(ast, { retainLines: true }, code)
|
|
212
|
+
return {
|
|
213
|
+
code: output.code,
|
|
214
|
+
map: output.map,
|
|
215
|
+
}
|
|
216
|
+
},
|
|
217
|
+
}
|
|
218
|
+
}
|