@rpgjs/vite 5.0.0-alpha.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/README.md +66 -0
- package/dist/directive-plugin.d.ts +5 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +8501 -0
- package/dist/index.js.map +1 -0
- package/dist/module-config.d.ts +1 -0
- package/dist/remove-imports-plugin.d.ts +36 -0
- package/dist/tiled-map-folder-plugin.d.ts +48 -0
- package/package.json +32 -0
- package/src/directive-plugin.ts +80 -0
- package/src/index.ts +4 -0
- package/src/module-config.ts +122 -0
- package/src/remove-imports-plugin.ts +164 -0
- package/src/tiled-map-folder-plugin.ts +224 -0
- package/tests/directive-plugin.spec.ts +34 -0
- package/tsconfig.json +17 -0
- package/vite.config.ts +43 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const rpgjsModuleViteConfig: () => import('vite').UserConfigFnPromise;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Plugin } from 'vite';
|
|
2
|
+
export interface RemoveImportsPluginOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Array of patterns to match against import sources
|
|
5
|
+
* Can be strings (exact match) or RegExp objects
|
|
6
|
+
*/
|
|
7
|
+
patterns: (string | RegExp)[];
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Vite plugin that replaces import statements with const declarations set to null
|
|
11
|
+
*
|
|
12
|
+
* This plugin analyzes JavaScript/TypeScript files and replaces import statements
|
|
13
|
+
* whose source matches any of the provided patterns with const declarations set to null.
|
|
14
|
+
* This is useful for removing dependencies while maintaining variable declarations.
|
|
15
|
+
*
|
|
16
|
+
* @param options - Configuration options for the plugin
|
|
17
|
+
* @returns Vite plugin object
|
|
18
|
+
*
|
|
19
|
+
* @example
|
|
20
|
+
* ```typescript
|
|
21
|
+
* // Replace specific imports
|
|
22
|
+
* removeImportsPlugin({
|
|
23
|
+
* patterns: ['react', 'vue', '@types/node']
|
|
24
|
+
* })
|
|
25
|
+
*
|
|
26
|
+
* // Replace imports using regex patterns
|
|
27
|
+
* removeImportsPlugin({
|
|
28
|
+
* patterns: [/^@types\//, /^react-/, 'lodash']
|
|
29
|
+
* })
|
|
30
|
+
*
|
|
31
|
+
* // Transform example:
|
|
32
|
+
* // import React, { useState } from 'react';
|
|
33
|
+
* // becomes: const React = null; const useState = null;
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export declare function removeImportsPlugin(options: RemoveImportsPluginOptions): Plugin;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { Plugin } from 'vite';
|
|
2
|
+
export interface DataFolderPluginOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Source folder containing the data files (TMX, TSX, images)
|
|
5
|
+
*/
|
|
6
|
+
sourceFolder: string;
|
|
7
|
+
/**
|
|
8
|
+
* Public path prefix for accessing the data files
|
|
9
|
+
* @default '/data'
|
|
10
|
+
*/
|
|
11
|
+
publicPath?: string;
|
|
12
|
+
/**
|
|
13
|
+
* Target folder in build output for the data files
|
|
14
|
+
* @default 'assets/data'
|
|
15
|
+
*/
|
|
16
|
+
buildOutputPath?: string;
|
|
17
|
+
/**
|
|
18
|
+
* File extensions to include
|
|
19
|
+
* @default ['.tmx', '.tsx', '.png', '.jpg', '.jpeg', '.gif', '.webp', '.svg']
|
|
20
|
+
*/
|
|
21
|
+
allowedExtensions?: string[];
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Vite plugin that serves a data folder in development mode and copies it to assets during build
|
|
25
|
+
*
|
|
26
|
+
* This plugin allows serving game data files (TMX maps, TSX tilesets, images) during development
|
|
27
|
+
* and automatically includes them in the build output for production deployment.
|
|
28
|
+
*
|
|
29
|
+
* @param options - Configuration options for the plugin
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```js
|
|
33
|
+
* // In vite.config.ts
|
|
34
|
+
* import { defineConfig } from 'vite';
|
|
35
|
+
* import { dataFolderPlugin } from '@rpgjs/vite';
|
|
36
|
+
*
|
|
37
|
+
* export default defineConfig({
|
|
38
|
+
* plugins: [
|
|
39
|
+
* dataFolderPlugin({
|
|
40
|
+
* sourceFolder: './game-data',
|
|
41
|
+
* publicPath: '/data',
|
|
42
|
+
* buildOutputPath: 'assets/data'
|
|
43
|
+
* })
|
|
44
|
+
* ]
|
|
45
|
+
* });
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
export declare function tiledMapFolderPlugin(options: DataFolderPluginOptions): Plugin;
|
package/package.json
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@rpgjs/vite",
|
|
3
|
+
"version": "5.0.0-alpha.0",
|
|
4
|
+
"main": "dist/index.js",
|
|
5
|
+
"types": "dist/index.d.ts",
|
|
6
|
+
"keywords": [],
|
|
7
|
+
"author": "",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"description": "Vite plugins for RPGJS",
|
|
10
|
+
"devDependencies": {
|
|
11
|
+
"@types/node": "^20.0.0",
|
|
12
|
+
"acorn": "^8.14.1",
|
|
13
|
+
"acorn-walk": "^8.3.4",
|
|
14
|
+
"magic-string": "^0.30.0",
|
|
15
|
+
"typescript": "^5.0.0",
|
|
16
|
+
"vite": "^6.2.5",
|
|
17
|
+
"vite-plugin-dts": "^4.5.3",
|
|
18
|
+
"vitest": "^3.1.1"
|
|
19
|
+
},
|
|
20
|
+
"type": "module",
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"@canvasengine/compiler": "2.0.0-beta.21"
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "vite build",
|
|
29
|
+
"dev": "vite build --watch",
|
|
30
|
+
"test": "vitest run"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { Plugin } from 'vite';
|
|
2
|
+
import MagicString from 'magic-string';
|
|
3
|
+
import { parse } from 'acorn';
|
|
4
|
+
import * as walk from 'acorn-walk';
|
|
5
|
+
|
|
6
|
+
export interface DirectivePluginOptions {
|
|
7
|
+
side: 'client' | 'server';
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function removeNode(s: MagicString, start: number, end: number) {
|
|
11
|
+
s.remove(start, end);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function isDirective(node: any): node is any {
|
|
15
|
+
return node.type === 'ExpressionStatement' &&
|
|
16
|
+
node.expression.type === 'Literal' &&
|
|
17
|
+
(node.expression.value === 'use client' || node.expression.value === 'use server');
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function directivePlugin(options: DirectivePluginOptions): Plugin {
|
|
21
|
+
return {
|
|
22
|
+
name: 'rpgjs-directive',
|
|
23
|
+
transform(code, id) {
|
|
24
|
+
const ast = parse(code, { ecmaVersion: 'latest', sourceType: 'module' }) as any;
|
|
25
|
+
const s = new MagicString(code);
|
|
26
|
+
|
|
27
|
+
let fileDirective: 'client' | 'server' | null = null;
|
|
28
|
+
if (ast.body.length && isDirective(ast.body[0])) {
|
|
29
|
+
fileDirective = (ast.body[0].expression.value as string).split(' ')[1] as 'client' | 'server';
|
|
30
|
+
if (fileDirective !== options.side) {
|
|
31
|
+
return {
|
|
32
|
+
code: 'export default null;',
|
|
33
|
+
map: { mappings: '' }
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
removeNode(s, ast.body[0].start, ast.body[0].end);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
walk.ancestor(ast, {
|
|
40
|
+
FunctionDeclaration(node: any, ancestors: any[]) {
|
|
41
|
+
if (node.body && node.body.body && node.body.body.length && isDirective(node.body.body[0])) {
|
|
42
|
+
const directive = (node.body.body[0].expression.value as string).split(' ')[1] as 'client' | 'server';
|
|
43
|
+
if (directive !== options.side) {
|
|
44
|
+
removeNode(s, node.start, node.end);
|
|
45
|
+
} else {
|
|
46
|
+
removeNode(s, node.body.body[0].start, node.body.body[0].end);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
FunctionExpression(node: any, ancestors: any[]) {
|
|
51
|
+
if (node.body && node.body.body && node.body.body.length && isDirective(node.body.body[0])) {
|
|
52
|
+
const directive = (node.body.body[0].expression.value as string).split(' ')[1] as 'client' | 'server';
|
|
53
|
+
if (directive !== options.side) {
|
|
54
|
+
removeNode(s, node.start, node.end);
|
|
55
|
+
} else {
|
|
56
|
+
removeNode(s, node.body.body[0].start, node.body.body[0].end);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
ArrowFunctionExpression(node: any, ancestors: any[]) {
|
|
61
|
+
if (node.body && node.body.body && node.body.body.length && isDirective(node.body.body[0])) {
|
|
62
|
+
const directive = (node.body.body[0].expression.value as string).split(' ')[1] as 'client' | 'server';
|
|
63
|
+
if (directive !== options.side) {
|
|
64
|
+
const decl = ancestors.slice().reverse().find((n: any) => n.type === 'VariableDeclaration');
|
|
65
|
+
if (decl) {
|
|
66
|
+
removeNode(s, decl.start, decl.end);
|
|
67
|
+
} else {
|
|
68
|
+
removeNode(s, node.start, node.end);
|
|
69
|
+
}
|
|
70
|
+
} else {
|
|
71
|
+
removeNode(s, node.body.body[0].start, node.body.body[0].end);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
return { code: s.toString(), map: s.generateMap({ hires: true }) };
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { tiledMapFolderPlugin, type DataFolderPluginOptions } from './tiled-map-folder-plugin';
|
|
2
|
+
export { rpgjsModuleViteConfig } from './module-config';
|
|
3
|
+
export { directivePlugin, type DirectivePluginOptions } from './directive-plugin';
|
|
4
|
+
export { removeImportsPlugin, type RemoveImportsPluginOptions } from './remove-imports-plugin';
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import { defineConfig, build } from "vite";
|
|
2
|
+
import canvasengine from "@canvasengine/compiler";
|
|
3
|
+
import dts from "vite-plugin-dts";
|
|
4
|
+
import { directivePlugin, removeImportsPlugin } from "./index";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Creates a build configuration for client or server side
|
|
8
|
+
*
|
|
9
|
+
* This function generates a standardized Vite build configuration that can be used
|
|
10
|
+
* for both client and server builds with different plugins and output directories.
|
|
11
|
+
*
|
|
12
|
+
* @param {Object} options - Build configuration options
|
|
13
|
+
* @param {string} options.side - The build side ('client' or 'server')
|
|
14
|
+
* @param {boolean} options.watch - Whether to enable watch mode
|
|
15
|
+
* @returns {Object} Vite build configuration object
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```typescript
|
|
19
|
+
* const clientConfig = createBuildConfig({ side: 'client', watch: false });
|
|
20
|
+
* const serverConfig = createBuildConfig({ side: 'server', watch: true });
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
function createBuildConfig({ side, watch }: { side: 'client' | 'server', watch: boolean }) {
|
|
24
|
+
const isClient = side === 'client';
|
|
25
|
+
|
|
26
|
+
const plugins = isClient
|
|
27
|
+
? [
|
|
28
|
+
canvasengine(),
|
|
29
|
+
removeImportsPlugin({ patterns: [/server/] }),
|
|
30
|
+
directivePlugin({ side: "client" }),
|
|
31
|
+
]
|
|
32
|
+
: [
|
|
33
|
+
removeImportsPlugin({ patterns: [/\.ce$/, /client/] }),
|
|
34
|
+
directivePlugin({ side: "server" }),
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
configFile: false as const, // Prevent using this config file
|
|
39
|
+
plugins: [
|
|
40
|
+
...plugins,
|
|
41
|
+
dts({
|
|
42
|
+
include: ['src/**/*.ts'],
|
|
43
|
+
outDir: 'dist'
|
|
44
|
+
})
|
|
45
|
+
],
|
|
46
|
+
build: {
|
|
47
|
+
watch: watch ? {} : undefined,
|
|
48
|
+
outDir: `dist/${side}`,
|
|
49
|
+
minify: false,
|
|
50
|
+
lib: {
|
|
51
|
+
entry: {
|
|
52
|
+
index: "src/index.ts",
|
|
53
|
+
},
|
|
54
|
+
fileName: "index",
|
|
55
|
+
formats: ["es" as const],
|
|
56
|
+
},
|
|
57
|
+
rollupOptions: {
|
|
58
|
+
external: [
|
|
59
|
+
/@rpgjs/,
|
|
60
|
+
"canvasengine",
|
|
61
|
+
"esbuild",
|
|
62
|
+
"@canvasengine/presets",
|
|
63
|
+
"rxjs",
|
|
64
|
+
],
|
|
65
|
+
output: {
|
|
66
|
+
preserveModules: true,
|
|
67
|
+
preserveModulesRoot: "src",
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Builds both client and server configurations in parallel
|
|
76
|
+
*
|
|
77
|
+
* This function creates and executes build configurations for both client and server
|
|
78
|
+
* sides simultaneously using Promise.all for better performance.
|
|
79
|
+
*
|
|
80
|
+
* @param {boolean} watch - Whether to enable watch mode for both builds
|
|
81
|
+
* @returns {Promise<void[]>} Promise that resolves when both builds are complete
|
|
82
|
+
*
|
|
83
|
+
* @example
|
|
84
|
+
* ```typescript
|
|
85
|
+
* // Build once
|
|
86
|
+
* await buildClientAndServer(false);
|
|
87
|
+
*
|
|
88
|
+
* // Build with watch mode
|
|
89
|
+
* await buildClientAndServer(true);
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
async function buildClientAndServer(watch: boolean = false) {
|
|
93
|
+
const clientBuild = build(createBuildConfig({ side: 'client', watch }));
|
|
94
|
+
const serverBuild = build(createBuildConfig({ side: 'server', watch }));
|
|
95
|
+
|
|
96
|
+
await Promise.all([clientBuild, serverBuild]);
|
|
97
|
+
|
|
98
|
+
console.log("✅ Build complete");
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const isWatchMode = process.argv.includes("--watch");
|
|
102
|
+
const isBuildCommand = process.argv.includes("build");
|
|
103
|
+
const isManualControl = isWatchMode && isBuildCommand;
|
|
104
|
+
|
|
105
|
+
export const rpgjsModuleViteConfig = () => {
|
|
106
|
+
return defineConfig(async ({ command }) => {
|
|
107
|
+
if (isManualControl) {
|
|
108
|
+
buildClientAndServer(true);
|
|
109
|
+
return {};
|
|
110
|
+
}
|
|
111
|
+
if (command === "build") {
|
|
112
|
+
console.log("👀 Building...");
|
|
113
|
+
await buildClientAndServer();
|
|
114
|
+
// Return empty config to prevent default build
|
|
115
|
+
process.exit(0);
|
|
116
|
+
} else {
|
|
117
|
+
return {
|
|
118
|
+
plugins: [],
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
});
|
|
122
|
+
};
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
import { Plugin } from 'vite';
|
|
2
|
+
import MagicString from 'magic-string';
|
|
3
|
+
import { parse } from 'acorn';
|
|
4
|
+
import * as walk from 'acorn-walk';
|
|
5
|
+
|
|
6
|
+
export interface RemoveImportsPluginOptions {
|
|
7
|
+
/**
|
|
8
|
+
* Array of patterns to match against import sources
|
|
9
|
+
* Can be strings (exact match) or RegExp objects
|
|
10
|
+
*/
|
|
11
|
+
patterns: (string | RegExp)[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Replaces a node in the source code using MagicString
|
|
16
|
+
* @param s - MagicString instance
|
|
17
|
+
* @param start - Start position of the node
|
|
18
|
+
* @param end - End position of the node
|
|
19
|
+
* @param replacement - The replacement string
|
|
20
|
+
*/
|
|
21
|
+
function replaceNode(s: MagicString, start: number, end: number, replacement: string) {
|
|
22
|
+
s.overwrite(start, end, replacement);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Checks if an import source matches any of the provided patterns
|
|
27
|
+
* @param source - The import source string
|
|
28
|
+
* @param patterns - Array of string or RegExp patterns
|
|
29
|
+
* @returns True if the source matches any pattern
|
|
30
|
+
*
|
|
31
|
+
* @example
|
|
32
|
+
* ```typescript
|
|
33
|
+
* matchesPattern('react', ['react', /^@types/]) // true
|
|
34
|
+
* matchesPattern('@types/node', ['react', /^@types/]) // true
|
|
35
|
+
* matchesPattern('lodash', ['react', /^@types/]) // false
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
function matchesPattern(source: string, patterns: (string | RegExp)[]): boolean {
|
|
39
|
+
return patterns.some(pattern => {
|
|
40
|
+
if (typeof pattern === 'string') {
|
|
41
|
+
return source === pattern;
|
|
42
|
+
} else if (pattern instanceof RegExp) {
|
|
43
|
+
return pattern.test(source);
|
|
44
|
+
}
|
|
45
|
+
return false;
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Generates const declarations from import specifiers
|
|
51
|
+
* @param node - The import declaration node
|
|
52
|
+
* @returns String with const declarations set to null
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* ```typescript
|
|
56
|
+
* // import React, { useState, useEffect } from 'react'
|
|
57
|
+
* // becomes: const React = null; const useState = null; const useEffect = null;
|
|
58
|
+
*
|
|
59
|
+
* // import * as utils from 'utils'
|
|
60
|
+
* // becomes: const utils = null;
|
|
61
|
+
*
|
|
62
|
+
* // import 'side-effect-module'
|
|
63
|
+
* // becomes: // removed import: side-effect-module
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
function generateConstDeclarations(node: any): string {
|
|
67
|
+
const declarations: string[] = [];
|
|
68
|
+
|
|
69
|
+
if (node.specifiers && node.specifiers.length > 0) {
|
|
70
|
+
for (const specifier of node.specifiers) {
|
|
71
|
+
if (specifier.type === 'ImportDefaultSpecifier') {
|
|
72
|
+
// import React from 'react' -> const React = null;
|
|
73
|
+
declarations.push(`const ${specifier.local.name} = null;`);
|
|
74
|
+
} else if (specifier.type === 'ImportSpecifier') {
|
|
75
|
+
// import { useState } from 'react' -> const useState = null;
|
|
76
|
+
declarations.push(`const ${specifier.local.name} = null;`);
|
|
77
|
+
} else if (specifier.type === 'ImportNamespaceSpecifier') {
|
|
78
|
+
// import * as React from 'react' -> const React = null;
|
|
79
|
+
declarations.push(`const ${specifier.local.name} = null;`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
} else {
|
|
83
|
+
// Side-effect import like import 'module' -> comment
|
|
84
|
+
declarations.push(`// removed import: ${node.source.value}`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return declarations.join(' ');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Vite plugin that replaces import statements with const declarations set to null
|
|
92
|
+
*
|
|
93
|
+
* This plugin analyzes JavaScript/TypeScript files and replaces import statements
|
|
94
|
+
* whose source matches any of the provided patterns with const declarations set to null.
|
|
95
|
+
* This is useful for removing dependencies while maintaining variable declarations.
|
|
96
|
+
*
|
|
97
|
+
* @param options - Configuration options for the plugin
|
|
98
|
+
* @returns Vite plugin object
|
|
99
|
+
*
|
|
100
|
+
* @example
|
|
101
|
+
* ```typescript
|
|
102
|
+
* // Replace specific imports
|
|
103
|
+
* removeImportsPlugin({
|
|
104
|
+
* patterns: ['react', 'vue', '@types/node']
|
|
105
|
+
* })
|
|
106
|
+
*
|
|
107
|
+
* // Replace imports using regex patterns
|
|
108
|
+
* removeImportsPlugin({
|
|
109
|
+
* patterns: [/^@types\//, /^react-/, 'lodash']
|
|
110
|
+
* })
|
|
111
|
+
*
|
|
112
|
+
* // Transform example:
|
|
113
|
+
* // import React, { useState } from 'react';
|
|
114
|
+
* // becomes: const React = null; const useState = null;
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
export function removeImportsPlugin(options: RemoveImportsPluginOptions): Plugin {
|
|
118
|
+
return {
|
|
119
|
+
name: 'rpgjs-remove-imports',
|
|
120
|
+
transform(code, id) {
|
|
121
|
+
// Skip non-JS/TS files
|
|
122
|
+
if (!id.match(/\.(js|ts|jsx|tsx)$/)) {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
try {
|
|
127
|
+
const ast = parse(code, {
|
|
128
|
+
ecmaVersion: 'latest',
|
|
129
|
+
sourceType: 'module'
|
|
130
|
+
}) as any;
|
|
131
|
+
|
|
132
|
+
const s = new MagicString(code);
|
|
133
|
+
let hasChanges = false;
|
|
134
|
+
|
|
135
|
+
// Walk through the AST to find import declarations
|
|
136
|
+
walk.simple(ast, {
|
|
137
|
+
ImportDeclaration(node: any) {
|
|
138
|
+
const importSource = node.source.value;
|
|
139
|
+
|
|
140
|
+
if (matchesPattern(importSource, options.patterns)) {
|
|
141
|
+
const replacement = generateConstDeclarations(node);
|
|
142
|
+
replaceNode(s, node.start, node.end, replacement);
|
|
143
|
+
hasChanges = true;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// Only return transformed code if changes were made
|
|
149
|
+
if (hasChanges) {
|
|
150
|
+
return {
|
|
151
|
+
code: s.toString(),
|
|
152
|
+
map: s.generateMap({ hires: true })
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
return null;
|
|
157
|
+
} catch (error) {
|
|
158
|
+
// If parsing fails, return the original code
|
|
159
|
+
console.warn(`Failed to parse ${id}:`, error);
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
}
|