vike 0.4.249-commit-55681da → 0.4.249-commit-ec50591
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/dist/client/runtime-client-routing/history.d.ts +1 -1
- package/dist/client/runtime-client-routing/history.js +4 -11
- package/dist/client/runtime-client-routing/utils.d.ts +0 -1
- package/dist/client/runtime-client-routing/utils.js +0 -1
- package/dist/node/vite/index.js +5 -2
- package/dist/node/vite/plugins/pluginStaticReplace/applyStaticReplace.d.ts +123 -0
- package/dist/node/vite/plugins/pluginStaticReplace/applyStaticReplace.js +404 -0
- package/dist/node/vite/plugins/pluginStaticReplace/buildFilterRolldown.d.ts +13 -0
- package/dist/node/vite/plugins/pluginStaticReplace/buildFilterRolldown.js +105 -0
- package/dist/node/vite/plugins/pluginStaticReplace.d.ts +4 -0
- package/dist/node/vite/plugins/pluginStaticReplace.js +57 -0
- package/dist/node/vite/plugins/pluginViteConfigVikeExtensions.d.ts +2 -1
- package/dist/node/vite/plugins/pluginViteConfigVikeExtensions.js +1 -3
- package/dist/node/vite/shared/importString.d.ts +50 -0
- package/dist/node/vite/shared/importString.js +64 -0
- package/dist/node/vite/shared/resolveVikeConfigInternal/configDefinitionsBuiltIn.js +7 -0
- package/dist/node/vite/shared/resolveVikeConfigInternal/pointerImports.js +11 -18
- package/dist/server/runtime/renderPageServer/csp.js +2 -0
- package/dist/types/Config/ConfigResolved.d.ts +3 -2
- package/dist/types/Config.d.ts +38 -32
- package/dist/types/index.d.ts +2 -1
- package/dist/utils/PROJECT_VERSION.d.ts +1 -1
- package/dist/utils/PROJECT_VERSION.js +1 -1
- package/package.json +4 -2
|
@@ -18,7 +18,7 @@ type ScrollPosition = {
|
|
|
18
18
|
};
|
|
19
19
|
declare function saveScrollPosition(): void;
|
|
20
20
|
declare function pushHistoryState(url: string, overwriteLastHistoryEntry: boolean): void;
|
|
21
|
-
declare function replaceHistoryStateOriginal(state: unknown, url
|
|
21
|
+
declare function replaceHistoryStateOriginal(state: unknown, url?: Parameters<typeof window.history.replaceState>[2]): void;
|
|
22
22
|
type HistoryInfo = {
|
|
23
23
|
url: `/${string}`;
|
|
24
24
|
state: StateEnhanced;
|
|
@@ -4,7 +4,7 @@ export { onPopStateBegin };
|
|
|
4
4
|
export { saveScrollPosition };
|
|
5
5
|
export { initHistory };
|
|
6
6
|
import { getCurrentUrl } from '../shared/getCurrentUrl.js';
|
|
7
|
-
import { assert, assertUsage, getGlobalObject, isObject,
|
|
7
|
+
import { assert, assertUsage, getGlobalObject, isObject, redirectHard } from './utils.js';
|
|
8
8
|
const globalObject = getGlobalObject('history.ts', {
|
|
9
9
|
monkeyPatched: false,
|
|
10
10
|
previous: undefined,
|
|
@@ -102,26 +102,19 @@ function monkeyPatchHistoryAPI() {
|
|
|
102
102
|
triggeredBy: 'user',
|
|
103
103
|
},
|
|
104
104
|
};
|
|
105
|
-
assertIsEnhanced(state);
|
|
106
105
|
funcOriginal(state, ...rest);
|
|
107
|
-
|
|
108
|
-
assert(isEqual(state, window.history.state));
|
|
106
|
+
assertIsEnhanced(window.history.state);
|
|
109
107
|
globalObject.previous = getHistoryInfo();
|
|
110
108
|
// Workaround https://github.com/vikejs/vike/issues/2504#issuecomment-3149764736
|
|
111
109
|
queueMicrotask(() => {
|
|
112
|
-
if (
|
|
110
|
+
if (isEnhanced(window.history.state))
|
|
113
111
|
return;
|
|
114
112
|
Object.assign(state, window.history.state);
|
|
115
|
-
|
|
116
|
-
replaceHistoryStateOriginal(state, rest[1]);
|
|
117
|
-
assert(isEqual(state, window.history.state));
|
|
113
|
+
replaceHistoryStateOriginal(state);
|
|
118
114
|
});
|
|
119
115
|
};
|
|
120
116
|
});
|
|
121
117
|
}
|
|
122
|
-
function isEqual(state1, state2) {
|
|
123
|
-
return deepEqual(state1?.vike, state2?.vike);
|
|
124
|
-
}
|
|
125
118
|
function isEnhanced(state) {
|
|
126
119
|
if (state?.vike) {
|
|
127
120
|
/* We don't use the assert() below to save client-side KBs.
|
|
@@ -21,5 +21,4 @@ export * from '../../utils/PROJECT_VERSION.js';
|
|
|
21
21
|
export * from '../../utils/genPromise.js';
|
|
22
22
|
export * from '../../utils/catchInfiniteLoop.js';
|
|
23
23
|
export * from '../../utils/changeEnumerable.js';
|
|
24
|
-
export * from '../../utils/deepEqual.js';
|
|
25
24
|
export * from '../../utils/cast.js';
|
|
@@ -25,5 +25,4 @@ export * from '../../utils/PROJECT_VERSION.js';
|
|
|
25
25
|
export * from '../../utils/genPromise.js';
|
|
26
26
|
export * from '../../utils/catchInfiniteLoop.js';
|
|
27
27
|
export * from '../../utils/changeEnumerable.js';
|
|
28
|
-
export * from '../../utils/deepEqual.js';
|
|
29
28
|
export * from '../../utils/cast.js';
|
package/dist/node/vite/index.js
CHANGED
|
@@ -22,6 +22,7 @@ import { pluginWorkaroundCssModuleHmr } from './plugins/pluginWorkaroundCssModul
|
|
|
22
22
|
import { pluginWorkaroundVite6HmrRegression } from './plugins/pluginWorkaroundVite6HmrRegression.js';
|
|
23
23
|
import { pluginReplaceConstantsPageContext } from './plugins/pluginReplaceConstantsPageContext.js';
|
|
24
24
|
import { pluginReplaceConstantsGlobalThis } from './plugins/pluginReplaceConstantsGlobalThis.js';
|
|
25
|
+
import { pluginStaticReplace } from './plugins/pluginStaticReplace.js';
|
|
25
26
|
import { pluginViteRPC } from './plugins/non-runnable-dev/pluginViteRPC.js';
|
|
26
27
|
import { pluginBuildApp } from './plugins/build/pluginBuildApp.js';
|
|
27
28
|
import { pluginDistPackageJsonFile } from './plugins/build/pluginDistPackageJsonFile.js';
|
|
@@ -33,7 +34,7 @@ import { pluginModuleBanner } from './plugins/build/pluginModuleBanner.js';
|
|
|
33
34
|
import { pluginReplaceConstantsNonRunnableDev } from './plugins/non-runnable-dev/pluginReplaceConstantsNonRunnableDev.js';
|
|
34
35
|
import { isVikeCliOrApi } from '../../shared-server-node/api-context.js';
|
|
35
36
|
import { pluginViteConfigVikeExtensions } from './plugins/pluginViteConfigVikeExtensions.js';
|
|
36
|
-
import { isOnlyResolvingUserConfig } from '../api/resolveViteConfigFromUser.js';
|
|
37
|
+
import { getVikeConfigInternalEarly, isOnlyResolvingUserConfig } from '../api/resolveViteConfigFromUser.js';
|
|
37
38
|
// We don't call this in ./onLoad.ts to avoid a cyclic dependency with utils.ts
|
|
38
39
|
setGetClientEntrySrcDev(getClientEntrySrcDev);
|
|
39
40
|
assertIsNotProductionRuntime();
|
|
@@ -42,6 +43,7 @@ function plugin(vikeVitePluginOptions = {}) {
|
|
|
42
43
|
const promise = (async () => {
|
|
43
44
|
if (skip())
|
|
44
45
|
return [];
|
|
46
|
+
const vikeConfig = await getVikeConfigInternalEarly();
|
|
45
47
|
const plugins = [
|
|
46
48
|
...pluginCommon(vikeVitePluginOptions),
|
|
47
49
|
...pluginVirtualFiles(),
|
|
@@ -58,8 +60,9 @@ function plugin(vikeVitePluginOptions = {}) {
|
|
|
58
60
|
...pluginWorkaroundVite6HmrRegression(),
|
|
59
61
|
...pluginReplaceConstantsPageContext(),
|
|
60
62
|
...pluginReplaceConstantsGlobalThis(),
|
|
63
|
+
...pluginStaticReplace(vikeConfig),
|
|
61
64
|
...pluginNonRunnabeDev(),
|
|
62
|
-
...(await pluginViteConfigVikeExtensions()),
|
|
65
|
+
...(await pluginViteConfigVikeExtensions(vikeConfig)),
|
|
63
66
|
];
|
|
64
67
|
return plugins;
|
|
65
68
|
})();
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
export { applyStaticReplace };
|
|
2
|
+
export type { StaticReplace };
|
|
3
|
+
/**
|
|
4
|
+
* Condition to match an argument value.
|
|
5
|
+
* - string starting with 'import:' matches an imported identifier
|
|
6
|
+
* - { prop, equals } matches a property value inside an object argument
|
|
7
|
+
* - { call, args } matches a call expression with specific arguments
|
|
8
|
+
* - { member, object, property } matches a member expression like $setup["ClientOnly"]
|
|
9
|
+
*/
|
|
10
|
+
type ArgCondition = string | {
|
|
11
|
+
prop: string;
|
|
12
|
+
equals: unknown;
|
|
13
|
+
} | {
|
|
14
|
+
call: string;
|
|
15
|
+
args?: Record<number, ArgCondition>;
|
|
16
|
+
} | {
|
|
17
|
+
member: true;
|
|
18
|
+
object: string;
|
|
19
|
+
property: string | ArgCondition;
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Target for replace operation.
|
|
23
|
+
*/
|
|
24
|
+
type ReplaceTarget = {
|
|
25
|
+
with: unknown;
|
|
26
|
+
} | {
|
|
27
|
+
arg: number;
|
|
28
|
+
prop: string;
|
|
29
|
+
with: unknown;
|
|
30
|
+
} | {
|
|
31
|
+
arg: number;
|
|
32
|
+
with: unknown;
|
|
33
|
+
} | {
|
|
34
|
+
argsFrom: number;
|
|
35
|
+
with: unknown;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Target for remove operation.
|
|
39
|
+
*/
|
|
40
|
+
type RemoveTarget = {
|
|
41
|
+
arg: number;
|
|
42
|
+
prop: string;
|
|
43
|
+
} | {
|
|
44
|
+
arg: number;
|
|
45
|
+
} | {
|
|
46
|
+
argsFrom: number;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* Rule for static replacement/removal.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* // jsx/jsxs/jsxDEV: replace children prop with null
|
|
53
|
+
* {
|
|
54
|
+
* env: 'server',
|
|
55
|
+
* type: 'call',
|
|
56
|
+
* match: {
|
|
57
|
+
* function: ['jsx', 'jsxs', 'jsxDEV'],
|
|
58
|
+
* args: { 0: 'import:vike-react/ClientOnly:ClientOnly' }
|
|
59
|
+
* },
|
|
60
|
+
* replace: { arg: 1, prop: 'children', with: null }
|
|
61
|
+
* }
|
|
62
|
+
*
|
|
63
|
+
* @example
|
|
64
|
+
* // createElement: remove all children (args from index 2)
|
|
65
|
+
* {
|
|
66
|
+
* env: 'server',
|
|
67
|
+
* type: 'call',
|
|
68
|
+
* match: {
|
|
69
|
+
* function: 'createElement',
|
|
70
|
+
* args: { 0: 'import:vike-react/ClientOnly:ClientOnly' }
|
|
71
|
+
* },
|
|
72
|
+
* remove: { argsFrom: 2 }
|
|
73
|
+
* }
|
|
74
|
+
*
|
|
75
|
+
* @example
|
|
76
|
+
* // ssrRenderComponent: match nested call expression and remove default slot
|
|
77
|
+
* {
|
|
78
|
+
* env: 'server',
|
|
79
|
+
* type: 'call',
|
|
80
|
+
* match: {
|
|
81
|
+
* function: 'import:vue/server-renderer:ssrRenderComponent',
|
|
82
|
+
* args: {
|
|
83
|
+
* 0: {
|
|
84
|
+
* call: 'import:vue:unref',
|
|
85
|
+
* args: { 0: 'import:vike-vue/ClientOnly:ClientOnly' }
|
|
86
|
+
* }
|
|
87
|
+
* }
|
|
88
|
+
* },
|
|
89
|
+
* remove: { arg: 2, prop: 'default' }
|
|
90
|
+
* }
|
|
91
|
+
*/
|
|
92
|
+
type StaticReplace = {
|
|
93
|
+
/** Type of transformation - currently only 'call' is supported, but can be extended in the future */
|
|
94
|
+
type?: 'call';
|
|
95
|
+
/** Environment filter: 'client' = client only, 'server' = everything except client */
|
|
96
|
+
env?: 'server' | 'client';
|
|
97
|
+
/** Match criteria */
|
|
98
|
+
match: {
|
|
99
|
+
/**
|
|
100
|
+
* Function name(s) to match.
|
|
101
|
+
* - Plain string: matches function name directly (e.g., 'jsx')
|
|
102
|
+
* - Import string: 'import:importPath:exportName' (e.g., 'import:react/jsx-runtime:jsx')
|
|
103
|
+
*/
|
|
104
|
+
function: string | string[];
|
|
105
|
+
/** Conditions on arguments: index -> condition */
|
|
106
|
+
args?: Record<number, ArgCondition>;
|
|
107
|
+
};
|
|
108
|
+
/** Replace target (optional) */
|
|
109
|
+
replace?: ReplaceTarget;
|
|
110
|
+
/** Remove target (optional) */
|
|
111
|
+
remove?: RemoveTarget;
|
|
112
|
+
};
|
|
113
|
+
type TransformResult = {
|
|
114
|
+
code: string;
|
|
115
|
+
map: any;
|
|
116
|
+
} | null;
|
|
117
|
+
type TransformInput = {
|
|
118
|
+
code: string;
|
|
119
|
+
id: string;
|
|
120
|
+
env: string;
|
|
121
|
+
options: StaticReplace[];
|
|
122
|
+
};
|
|
123
|
+
declare function applyStaticReplace({ code, id, env, options }: TransformInput): Promise<TransformResult>;
|
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
export { applyStaticReplace };
|
|
2
|
+
import { transformAsync } from '@babel/core';
|
|
3
|
+
import * as t from '@babel/types';
|
|
4
|
+
import { parseImportString } from '../../shared/importString.js';
|
|
5
|
+
async function applyStaticReplace({ code, id, env, options }) {
|
|
6
|
+
// 'server' means "not client" (covers ssr, cloudflare, etc.)
|
|
7
|
+
const filteredRules = options.filter((rule) => {
|
|
8
|
+
if (!rule.env)
|
|
9
|
+
return true;
|
|
10
|
+
if (rule.env === 'client')
|
|
11
|
+
return env === 'client';
|
|
12
|
+
if (rule.env === 'server')
|
|
13
|
+
return env !== 'client';
|
|
14
|
+
return false;
|
|
15
|
+
});
|
|
16
|
+
if (filteredRules.length === 0) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
try {
|
|
20
|
+
const state = {
|
|
21
|
+
modified: false,
|
|
22
|
+
imports: new Map(),
|
|
23
|
+
alreadyUnreferenced: new Set(),
|
|
24
|
+
};
|
|
25
|
+
const result = await transformAsync(code, {
|
|
26
|
+
filename: id,
|
|
27
|
+
ast: true,
|
|
28
|
+
sourceMaps: true,
|
|
29
|
+
plugins: [collectImportsPlugin(state), applyRulesPlugin(state, filteredRules), removeUnreferencedPlugin(state)],
|
|
30
|
+
});
|
|
31
|
+
if (!result?.code || !state.modified) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
return { code: result.code, map: result.map };
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
console.error(`Error transforming ${id}:`, error);
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// ============================================================================
|
|
42
|
+
// Helpers
|
|
43
|
+
// ============================================================================
|
|
44
|
+
function valueToAst(value) {
|
|
45
|
+
if (value === null)
|
|
46
|
+
return t.nullLiteral();
|
|
47
|
+
if (value === undefined)
|
|
48
|
+
return t.identifier('undefined');
|
|
49
|
+
if (typeof value === 'string')
|
|
50
|
+
return t.stringLiteral(value);
|
|
51
|
+
if (typeof value === 'number')
|
|
52
|
+
return t.numericLiteral(value);
|
|
53
|
+
if (typeof value === 'boolean')
|
|
54
|
+
return t.booleanLiteral(value);
|
|
55
|
+
return t.callExpression(t.memberExpression(t.identifier('JSON'), t.identifier('parse')), [
|
|
56
|
+
t.stringLiteral(JSON.stringify(value)),
|
|
57
|
+
]);
|
|
58
|
+
}
|
|
59
|
+
function getCalleeName(callee) {
|
|
60
|
+
if (t.isIdentifier(callee))
|
|
61
|
+
return callee.name;
|
|
62
|
+
if (t.isMemberExpression(callee) && t.isIdentifier(callee.property))
|
|
63
|
+
return callee.property.name;
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Check if an identifier matches an import condition
|
|
68
|
+
*/
|
|
69
|
+
function matchesImport(arg, parsed, state) {
|
|
70
|
+
if (!t.isIdentifier(arg))
|
|
71
|
+
return false;
|
|
72
|
+
const imported = state.imports.get(arg.name);
|
|
73
|
+
if (!imported)
|
|
74
|
+
return false;
|
|
75
|
+
return imported.importPath === parsed.importPath && imported.exportName === parsed.exportName;
|
|
76
|
+
}
|
|
77
|
+
// ============================================================================
|
|
78
|
+
// Babel plugins
|
|
79
|
+
// ============================================================================
|
|
80
|
+
/**
|
|
81
|
+
* Collect all imports: localName -> { importPath, exportName }
|
|
82
|
+
*/
|
|
83
|
+
function collectImportsPlugin(state) {
|
|
84
|
+
return {
|
|
85
|
+
visitor: {
|
|
86
|
+
ImportDeclaration(path) {
|
|
87
|
+
const importPath = path.node.source.value;
|
|
88
|
+
for (const specifier of path.node.specifiers) {
|
|
89
|
+
let exportName;
|
|
90
|
+
if (t.isImportSpecifier(specifier) && t.isIdentifier(specifier.imported)) {
|
|
91
|
+
exportName = specifier.imported.name;
|
|
92
|
+
}
|
|
93
|
+
else if (t.isImportDefaultSpecifier(specifier)) {
|
|
94
|
+
exportName = 'default';
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
state.imports.set(specifier.local.name, { importPath, exportName });
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Apply replacement rules to matching call expressions
|
|
107
|
+
*/
|
|
108
|
+
function applyRulesPlugin(state, rules) {
|
|
109
|
+
return {
|
|
110
|
+
visitor: {
|
|
111
|
+
CallExpression(path) {
|
|
112
|
+
const calleeName = getCalleeName(path.node.callee);
|
|
113
|
+
if (!calleeName)
|
|
114
|
+
return;
|
|
115
|
+
for (const rule of rules) {
|
|
116
|
+
if (!matchesRule(path, rule, calleeName, state))
|
|
117
|
+
continue;
|
|
118
|
+
if (rule.replace) {
|
|
119
|
+
applyReplace(path, rule.replace, state);
|
|
120
|
+
}
|
|
121
|
+
else if (rule.remove) {
|
|
122
|
+
applyRemove(path, rule.remove, state);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
},
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Check if a call expression matches a rule
|
|
131
|
+
*/
|
|
132
|
+
function matchesRule(path, staticReplace, calleeName, state) {
|
|
133
|
+
const { match } = staticReplace;
|
|
134
|
+
// Check callee name
|
|
135
|
+
const functions = Array.isArray(match.function) ? match.function : [match.function];
|
|
136
|
+
if (!matchesCallee(path.node.callee, calleeName, functions, state))
|
|
137
|
+
return false;
|
|
138
|
+
// Check argument conditions
|
|
139
|
+
if (match.args) {
|
|
140
|
+
for (const [indexStr, condition] of Object.entries(match.args)) {
|
|
141
|
+
const index = Number(indexStr);
|
|
142
|
+
const arg = path.node.arguments[index];
|
|
143
|
+
if (!arg)
|
|
144
|
+
return false;
|
|
145
|
+
if (!matchesCondition(arg, condition, state))
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Check if callee matches any of the function patterns
|
|
153
|
+
*/
|
|
154
|
+
function matchesCallee(callee, calleeName, functions, state) {
|
|
155
|
+
for (const fn of functions) {
|
|
156
|
+
const parsed = parseImportString(fn);
|
|
157
|
+
if (parsed) {
|
|
158
|
+
// Import string: check if callee is an imported identifier
|
|
159
|
+
if (t.isIdentifier(callee)) {
|
|
160
|
+
const imported = state.imports.get(callee.name);
|
|
161
|
+
if (imported && imported.importPath === parsed.importPath && imported.exportName === parsed.exportName) {
|
|
162
|
+
return true;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
// Import string: check member expression
|
|
166
|
+
if (t.isMemberExpression(callee) && t.isIdentifier(callee.object) && t.isIdentifier(callee.property)) {
|
|
167
|
+
const imported = state.imports.get(callee.object.name);
|
|
168
|
+
if (imported &&
|
|
169
|
+
imported.importPath === parsed.importPath &&
|
|
170
|
+
imported.exportName === 'default' &&
|
|
171
|
+
callee.property.name === parsed.exportName) {
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
else {
|
|
177
|
+
// Plain string: match function name directly
|
|
178
|
+
if (calleeName === fn)
|
|
179
|
+
return true;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Check if an argument matches a condition
|
|
186
|
+
*/
|
|
187
|
+
function matchesCondition(arg, condition, state) {
|
|
188
|
+
// String condition
|
|
189
|
+
if (typeof condition === 'string') {
|
|
190
|
+
// Import condition: 'import:importPath:exportName'
|
|
191
|
+
const parsed = parseImportString(condition);
|
|
192
|
+
if (parsed) {
|
|
193
|
+
return t.isExpression(arg) && matchesImport(arg, parsed, state);
|
|
194
|
+
}
|
|
195
|
+
// Plain string: match string literal or identifier name
|
|
196
|
+
if (t.isStringLiteral(arg))
|
|
197
|
+
return arg.value === condition;
|
|
198
|
+
if (t.isIdentifier(arg))
|
|
199
|
+
return arg.name === condition;
|
|
200
|
+
return false;
|
|
201
|
+
}
|
|
202
|
+
// Call expression condition: match call with specific arguments
|
|
203
|
+
if ('call' in condition) {
|
|
204
|
+
if (!t.isCallExpression(arg))
|
|
205
|
+
return false;
|
|
206
|
+
const calleeName = getCalleeName(arg.callee);
|
|
207
|
+
if (!calleeName)
|
|
208
|
+
return false;
|
|
209
|
+
// Check if callee matches
|
|
210
|
+
const parsed = parseImportString(condition.call);
|
|
211
|
+
if (parsed) {
|
|
212
|
+
// Import string: check if callee is an imported identifier
|
|
213
|
+
if (!t.isIdentifier(arg.callee))
|
|
214
|
+
return false;
|
|
215
|
+
const imported = state.imports.get(arg.callee.name);
|
|
216
|
+
if (!imported || imported.importPath !== parsed.importPath || imported.exportName !== parsed.exportName) {
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
// Plain string: match function name directly
|
|
222
|
+
if (calleeName !== condition.call)
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
// Check argument conditions
|
|
226
|
+
if (condition.args) {
|
|
227
|
+
for (const [indexStr, argCondition] of Object.entries(condition.args)) {
|
|
228
|
+
const index = Number(indexStr);
|
|
229
|
+
const nestedArg = arg.arguments[index];
|
|
230
|
+
if (!nestedArg)
|
|
231
|
+
return false;
|
|
232
|
+
if (!matchesCondition(nestedArg, argCondition, state))
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
return true;
|
|
237
|
+
}
|
|
238
|
+
// Member expression condition: match $setup["ClientOnly"]
|
|
239
|
+
if ('member' in condition) {
|
|
240
|
+
if (!t.isMemberExpression(arg))
|
|
241
|
+
return false;
|
|
242
|
+
// Check object
|
|
243
|
+
if (!t.isIdentifier(arg.object) || arg.object.name !== condition.object)
|
|
244
|
+
return false;
|
|
245
|
+
// Check property
|
|
246
|
+
if (typeof condition.property === 'string') {
|
|
247
|
+
// Simple string property
|
|
248
|
+
if (t.isIdentifier(arg.property) && !arg.computed) {
|
|
249
|
+
return arg.property.name === condition.property;
|
|
250
|
+
}
|
|
251
|
+
if (t.isStringLiteral(arg.property) && arg.computed) {
|
|
252
|
+
return arg.property.value === condition.property;
|
|
253
|
+
}
|
|
254
|
+
return false;
|
|
255
|
+
}
|
|
256
|
+
else {
|
|
257
|
+
// Nested condition on property (for future extensibility)
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
// Object condition: match prop value inside an object argument
|
|
262
|
+
if (!t.isObjectExpression(arg))
|
|
263
|
+
return false;
|
|
264
|
+
for (const prop of arg.properties) {
|
|
265
|
+
if (!t.isObjectProperty(prop))
|
|
266
|
+
continue;
|
|
267
|
+
if (!t.isIdentifier(prop.key) || prop.key.name !== condition.prop)
|
|
268
|
+
continue;
|
|
269
|
+
// Check value
|
|
270
|
+
if (condition.equals === null && t.isNullLiteral(prop.value))
|
|
271
|
+
return true;
|
|
272
|
+
if (condition.equals === true && t.isBooleanLiteral(prop.value) && prop.value.value === true)
|
|
273
|
+
return true;
|
|
274
|
+
if (condition.equals === false && t.isBooleanLiteral(prop.value) && prop.value.value === false)
|
|
275
|
+
return true;
|
|
276
|
+
if (typeof condition.equals === 'string' && t.isStringLiteral(prop.value) && prop.value.value === condition.equals)
|
|
277
|
+
return true;
|
|
278
|
+
if (typeof condition.equals === 'number' && t.isNumericLiteral(prop.value) && prop.value.value === condition.equals)
|
|
279
|
+
return true;
|
|
280
|
+
}
|
|
281
|
+
return false;
|
|
282
|
+
}
|
|
283
|
+
/**
|
|
284
|
+
* Apply a replacement to a call expression
|
|
285
|
+
*/
|
|
286
|
+
function applyReplace(path, replace, state) {
|
|
287
|
+
// Replace the entire call expression
|
|
288
|
+
if (!('arg' in replace) && !('argsFrom' in replace)) {
|
|
289
|
+
path.replaceWith(valueToAst(replace.with));
|
|
290
|
+
state.modified = true;
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
// Replace a prop inside an object argument
|
|
294
|
+
if ('arg' in replace && 'prop' in replace) {
|
|
295
|
+
const arg = path.node.arguments[replace.arg];
|
|
296
|
+
if (!t.isObjectExpression(arg))
|
|
297
|
+
return;
|
|
298
|
+
for (const prop of arg.properties) {
|
|
299
|
+
if (!t.isObjectProperty(prop))
|
|
300
|
+
continue;
|
|
301
|
+
if (!t.isIdentifier(prop.key) || prop.key.name !== replace.prop)
|
|
302
|
+
continue;
|
|
303
|
+
prop.value = valueToAst(replace.with);
|
|
304
|
+
state.modified = true;
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
// Replace entire argument
|
|
309
|
+
else if ('arg' in replace) {
|
|
310
|
+
if (path.node.arguments.length > replace.arg) {
|
|
311
|
+
path.node.arguments[replace.arg] = valueToAst(replace.with);
|
|
312
|
+
state.modified = true;
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
// Replace all args from index onwards with a single value
|
|
316
|
+
else if ('argsFrom' in replace) {
|
|
317
|
+
if (path.node.arguments.length > replace.argsFrom) {
|
|
318
|
+
path.node.arguments = [...path.node.arguments.slice(0, replace.argsFrom), valueToAst(replace.with)];
|
|
319
|
+
state.modified = true;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
/**
|
|
324
|
+
* Apply a removal to a call expression
|
|
325
|
+
*/
|
|
326
|
+
function applyRemove(path, remove, state) {
|
|
327
|
+
// Remove a prop inside an object argument
|
|
328
|
+
if ('prop' in remove) {
|
|
329
|
+
const arg = path.node.arguments[remove.arg];
|
|
330
|
+
if (!t.isObjectExpression(arg))
|
|
331
|
+
return;
|
|
332
|
+
const index = arg.properties.findIndex((prop) => {
|
|
333
|
+
// Check ObjectProperty with Identifier key
|
|
334
|
+
if (t.isObjectProperty(prop) && t.isIdentifier(prop.key) && prop.key.name === remove.prop) {
|
|
335
|
+
return true;
|
|
336
|
+
}
|
|
337
|
+
// Check ObjectMethod (getter/setter)
|
|
338
|
+
if (t.isObjectMethod(prop) && t.isIdentifier(prop.key) && prop.key.name === remove.prop) {
|
|
339
|
+
return true;
|
|
340
|
+
}
|
|
341
|
+
return false;
|
|
342
|
+
});
|
|
343
|
+
if (index !== -1) {
|
|
344
|
+
arg.properties.splice(index, 1);
|
|
345
|
+
state.modified = true;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
// Remove entire argument
|
|
349
|
+
else if ('arg' in remove) {
|
|
350
|
+
if (path.node.arguments.length > remove.arg) {
|
|
351
|
+
path.node.arguments.splice(remove.arg, 1);
|
|
352
|
+
state.modified = true;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
// Remove all args from index onwards
|
|
356
|
+
else if ('argsFrom' in remove) {
|
|
357
|
+
if (path.node.arguments.length > remove.argsFrom) {
|
|
358
|
+
path.node.arguments = path.node.arguments.slice(0, remove.argsFrom);
|
|
359
|
+
state.modified = true;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Remove unreferenced bindings after modifications
|
|
365
|
+
*/
|
|
366
|
+
function removeUnreferencedPlugin(state) {
|
|
367
|
+
return {
|
|
368
|
+
visitor: {
|
|
369
|
+
Program: {
|
|
370
|
+
enter(program) {
|
|
371
|
+
for (const [name, binding] of Object.entries(program.scope.bindings)) {
|
|
372
|
+
if (!binding.referenced)
|
|
373
|
+
state.alreadyUnreferenced.add(name);
|
|
374
|
+
}
|
|
375
|
+
},
|
|
376
|
+
exit(program) {
|
|
377
|
+
if (!state.modified)
|
|
378
|
+
return;
|
|
379
|
+
removeUnreferenced(program, state.alreadyUnreferenced);
|
|
380
|
+
},
|
|
381
|
+
},
|
|
382
|
+
},
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
function removeUnreferenced(program, alreadyUnreferenced) {
|
|
386
|
+
for (;;) {
|
|
387
|
+
program.scope.crawl();
|
|
388
|
+
let removed = false;
|
|
389
|
+
for (const [name, binding] of Object.entries(program.scope.bindings)) {
|
|
390
|
+
if (binding.referenced || alreadyUnreferenced.has(name))
|
|
391
|
+
continue;
|
|
392
|
+
const parent = binding.path.parentPath;
|
|
393
|
+
if (parent?.isImportDeclaration() && parent.node.specifiers.length === 1) {
|
|
394
|
+
parent.remove();
|
|
395
|
+
}
|
|
396
|
+
else {
|
|
397
|
+
binding.path.remove();
|
|
398
|
+
}
|
|
399
|
+
removed = true;
|
|
400
|
+
}
|
|
401
|
+
if (!removed)
|
|
402
|
+
break;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export { buildFilterRolldown };
|
|
2
|
+
import type { StaticReplace } from './applyStaticReplace.js';
|
|
3
|
+
/**
|
|
4
|
+
* Build a filterRolldown from staticReplaceList by extracting all import strings.
|
|
5
|
+
* For a single entry, ALL import strings must be present (AND logic),
|
|
6
|
+
* except for call.match.function array which is OR logic.
|
|
7
|
+
* Between staticReplace entries it's OR logic.
|
|
8
|
+
*/
|
|
9
|
+
declare function buildFilterRolldown(staticReplaceList: StaticReplace[]): {
|
|
10
|
+
code: {
|
|
11
|
+
include: RegExp;
|
|
12
|
+
};
|
|
13
|
+
} | null;
|