@gtkx/cli 1.4.0 → 1.6.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 +1 -1
- package/dist/builder.d.ts +1 -0
- package/dist/builder.d.ts.map +1 -1
- package/dist/builder.js +2 -2
- package/dist/builder.js.map +1 -1
- package/dist/cli.js +4 -0
- package/dist/cli.js.map +1 -1
- package/dist/codegen/freshness.d.ts.map +1 -1
- package/dist/codegen/freshness.js +2 -1
- package/dist/codegen/freshness.js.map +1 -1
- package/dist/codegen/reference.d.ts +17 -0
- package/dist/codegen/reference.d.ts.map +1 -0
- package/dist/codegen/reference.js +37 -0
- package/dist/codegen/reference.js.map +1 -0
- package/dist/codegen/report.d.ts.map +1 -1
- package/dist/codegen/report.js +17 -3
- package/dist/codegen/report.js.map +1 -1
- package/dist/codegen/run-codegen.d.ts +4 -0
- package/dist/codegen/run-codegen.d.ts.map +1 -1
- package/dist/codegen/run-codegen.js +93 -27
- package/dist/codegen/run-codegen.js.map +1 -1
- package/dist/commands/codegen.js +1 -1
- package/dist/commands/codegen.js.map +1 -1
- package/dist/commands/docs.d.ts.map +1 -1
- package/dist/commands/docs.js +10 -16
- package/dist/commands/docs.js.map +1 -1
- package/dist/commands/mcp.d.ts +13 -0
- package/dist/commands/mcp.d.ts.map +1 -0
- package/dist/commands/mcp.js +68 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/deploy/freedesktop/localize.d.ts +6 -0
- package/dist/deploy/freedesktop/localize.d.ts.map +1 -0
- package/dist/deploy/freedesktop/localize.js +107 -0
- package/dist/deploy/freedesktop/localize.js.map +1 -0
- package/dist/deploy/install-hints.d.ts.map +1 -1
- package/dist/deploy/install-hints.js +5 -0
- package/dist/deploy/install-hints.js.map +1 -1
- package/dist/deploy/notices/gtkx.js +1 -1
- package/dist/deploy/notices/gtkx.js.map +1 -1
- package/dist/deploy/notices/libraries.d.ts.map +1 -1
- package/dist/deploy/notices/libraries.js +2 -1
- package/dist/deploy/notices/libraries.js.map +1 -1
- package/dist/deploy/payload/launcher.d.ts.map +1 -1
- package/dist/deploy/payload/launcher.js +2 -0
- package/dist/deploy/payload/launcher.js.map +1 -1
- package/dist/deploy/payload/stage.d.ts +1 -1
- package/dist/deploy/payload/stage.d.ts.map +1 -1
- package/dist/deploy/payload/stage.js +20 -11
- package/dist/deploy/payload/stage.js.map +1 -1
- package/dist/deploy/run-deploy.d.ts.map +1 -1
- package/dist/deploy/run-deploy.js +87 -15
- package/dist/deploy/run-deploy.js.map +1 -1
- package/dist/deploy/settings/libraries.d.ts.map +1 -1
- package/dist/deploy/settings/libraries.js +7 -9
- package/dist/deploy/settings/libraries.js.map +1 -1
- package/dist/deploy/targets/flatpak-source.d.ts.map +1 -1
- package/dist/deploy/targets/flatpak-source.js +21 -10
- package/dist/deploy/targets/flatpak-source.js.map +1 -1
- package/dist/deploy/tools.d.ts +6 -1
- package/dist/deploy/tools.d.ts.map +1 -1
- package/dist/deploy/tools.js +27 -4
- package/dist/deploy/tools.js.map +1 -1
- package/dist/dev/locale-dir.d.ts +3 -0
- package/dist/dev/locale-dir.d.ts.map +1 -0
- package/dist/dev/locale-dir.js +17 -0
- package/dist/dev/locale-dir.js.map +1 -0
- package/dist/dev/runner-main.d.ts.map +1 -1
- package/dist/dev/runner-main.js +4 -1
- package/dist/dev/runner-main.js.map +1 -1
- package/dist/dev/runner.d.ts.map +1 -1
- package/dist/dev/runner.js +6 -0
- package/dist/dev/runner.js.map +1 -1
- package/dist/dev/vite-dev-server.d.ts +1 -0
- package/dist/dev/vite-dev-server.d.ts.map +1 -1
- package/dist/dev/vite-dev-server.js.map +1 -1
- package/dist/i18n/binding-extractor.d.ts +30 -0
- package/dist/i18n/binding-extractor.d.ts.map +1 -0
- package/dist/i18n/binding-extractor.js +1324 -0
- package/dist/i18n/binding-extractor.js.map +1 -0
- package/dist/i18n/catalog-template.d.ts +3 -0
- package/dist/i18n/catalog-template.d.ts.map +1 -0
- package/dist/i18n/catalog-template.js +18 -0
- package/dist/i18n/catalog-template.js.map +1 -0
- package/dist/i18n/catalogs.d.ts +21 -0
- package/dist/i18n/catalogs.d.ts.map +1 -0
- package/dist/i18n/catalogs.js +139 -0
- package/dist/i18n/catalogs.js.map +1 -0
- package/dist/i18n/extractor-plugin.d.ts +8 -0
- package/dist/i18n/extractor-plugin.d.ts.map +1 -0
- package/dist/i18n/extractor-plugin.js +247 -0
- package/dist/i18n/extractor-plugin.js.map +1 -0
- package/dist/i18n/metadata-templates.d.ts +11 -0
- package/dist/i18n/metadata-templates.d.ts.map +1 -0
- package/dist/i18n/metadata-templates.js +11 -0
- package/dist/i18n/metadata-templates.js.map +1 -0
- package/dist/i18n/source-messages.d.ts +23 -0
- package/dist/i18n/source-messages.d.ts.map +1 -0
- package/dist/i18n/source-messages.js +403 -0
- package/dist/i18n/source-messages.js.map +1 -0
- package/dist/i18n/types.d.ts +10 -0
- package/dist/i18n/types.d.ts.map +1 -0
- package/dist/i18n/types.js +108 -0
- package/dist/i18n/types.js.map +1 -0
- package/dist/internal/agent-rules.d.ts +7 -0
- package/dist/internal/agent-rules.d.ts.map +1 -0
- package/dist/internal/agent-rules.js +106 -0
- package/dist/internal/agent-rules.js.map +1 -0
- package/dist/internal/docs-elements.d.ts +8 -0
- package/dist/internal/docs-elements.d.ts.map +1 -0
- package/dist/internal/docs-elements.js +12 -0
- package/dist/internal/docs-elements.js.map +1 -0
- package/dist/internal/source-imports.d.ts +5 -1
- package/dist/internal/source-imports.d.ts.map +1 -1
- package/dist/internal/source-imports.js +25 -47
- package/dist/internal/source-imports.js.map +1 -1
- package/dist/mcp/client-config.d.ts +22 -0
- package/dist/mcp/client-config.d.ts.map +1 -0
- package/dist/mcp/client-config.js +94 -0
- package/dist/mcp/client-config.js.map +1 -0
- package/dist/mcp/client.d.ts +0 -1
- package/dist/mcp/client.d.ts.map +1 -1
- package/dist/mcp/client.js +11 -39
- package/dist/mcp/client.js.map +1 -1
- package/dist/mcp/handlers.d.ts +2 -1
- package/dist/mcp/handlers.d.ts.map +1 -1
- package/dist/mcp/handlers.js.map +1 -1
- package/dist/settings/render.d.ts +4 -1
- package/dist/settings/render.d.ts.map +1 -1
- package/dist/settings/render.js +11 -2
- package/dist/settings/render.js.map +1 -1
- package/dist/settings/schema.d.ts.map +1 -1
- package/dist/settings/schema.js +3 -1
- package/dist/settings/schema.js.map +1 -1
- package/dist/vite-plugins/animated.d.ts +5 -0
- package/dist/vite-plugins/animated.d.ts.map +1 -0
- package/dist/vite-plugins/animated.js +325 -0
- package/dist/vite-plugins/animated.js.map +1 -0
- package/dist/vite-plugins/asset-imports.d.ts.map +1 -1
- package/dist/vite-plugins/asset-imports.js +4 -14
- package/dist/vite-plugins/asset-imports.js.map +1 -1
- package/dist/vite-plugins/i18n.d.ts +5 -0
- package/dist/vite-plugins/i18n.d.ts.map +1 -0
- package/dist/vite-plugins/i18n.js +88 -0
- package/dist/vite-plugins/i18n.js.map +1 -0
- package/dist/vite-plugins/index.d.ts +1 -1
- package/dist/vite-plugins/index.d.ts.map +1 -1
- package/dist/vite-plugins/index.js +5 -1
- package/dist/vite-plugins/index.js.map +1 -1
- package/dist/vite-plugins/resources.d.ts.map +1 -1
- package/dist/vite-plugins/resources.js +12 -57
- package/dist/vite-plugins/resources.js.map +1 -1
- package/dist/vite-plugins/self-contained.d.ts.map +1 -1
- package/dist/vite-plugins/self-contained.js +128 -135
- package/dist/vite-plugins/self-contained.js.map +1 -1
- package/dist/vite-plugins/settings-worker-env.js +2 -2
- package/dist/vite-plugins/settings-worker-env.js.map +1 -1
- package/dist/vite-plugins/settings.d.ts.map +1 -1
- package/dist/vite-plugins/settings.js +2 -1
- package/dist/vite-plugins/settings.js.map +1 -1
- package/package.json +11 -9
- package/src/builder.ts +3 -2
- package/src/cli.ts +5 -0
- package/src/codegen/freshness.ts +2 -1
- package/src/codegen/reference.ts +59 -0
- package/src/codegen/report.ts +18 -3
- package/src/codegen/run-codegen.ts +134 -31
- package/src/commands/codegen.ts +1 -1
- package/src/commands/docs.ts +10 -28
- package/src/commands/mcp.ts +81 -0
- package/src/deploy/freedesktop/localize.ts +144 -0
- package/src/deploy/install-hints.ts +5 -0
- package/src/deploy/notices/gtkx.ts +1 -1
- package/src/deploy/notices/libraries.ts +2 -1
- package/src/deploy/payload/launcher.ts +2 -0
- package/src/deploy/payload/stage.ts +35 -10
- package/src/deploy/run-deploy.ts +128 -13
- package/src/deploy/settings/libraries.ts +7 -10
- package/src/deploy/targets/flatpak-source.ts +33 -10
- package/src/deploy/tools.ts +36 -3
- package/src/dev/locale-dir.ts +22 -0
- package/src/dev/runner-main.ts +4 -1
- package/src/dev/runner.ts +8 -0
- package/src/dev/vite-dev-server.ts +1 -0
- package/src/i18n/binding-extractor.ts +1969 -0
- package/src/i18n/catalog-template.ts +24 -0
- package/src/i18n/catalogs.ts +216 -0
- package/src/i18n/extractor-plugin.ts +413 -0
- package/src/i18n/metadata-templates.ts +29 -0
- package/src/i18n/source-messages.ts +608 -0
- package/src/i18n/types.ts +154 -0
- package/src/internal/agent-rules.ts +135 -0
- package/src/internal/docs-elements.ts +21 -0
- package/src/internal/source-imports.ts +31 -60
- package/src/mcp/client-config.ts +130 -0
- package/src/mcp/client.ts +17 -44
- package/src/mcp/handlers.ts +7 -6
- package/src/settings/render.ts +16 -2
- package/src/settings/schema.ts +3 -1
- package/src/vite-plugins/animated.ts +486 -0
- package/src/vite-plugins/asset-imports.ts +5 -17
- package/src/vite-plugins/i18n.ts +146 -0
- package/src/vite-plugins/index.ts +5 -0
- package/src/vite-plugins/resources.ts +12 -74
- package/src/vite-plugins/self-contained.ts +150 -169
- package/src/vite-plugins/settings-worker-env.ts +2 -2
- package/src/vite-plugins/settings.ts +2 -1
package/src/mcp/handlers.ts
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
invalidRequestError,
|
|
4
4
|
methodNotFoundError,
|
|
5
5
|
type ParamsSchema,
|
|
6
|
+
type Result,
|
|
6
7
|
type ServerInitiatedMethod,
|
|
7
8
|
type ServerRequestParams,
|
|
8
9
|
ServerRequestParamsSchemas,
|
|
@@ -18,10 +19,10 @@ type HandlerContext = {
|
|
|
18
19
|
registry: WidgetRegistry;
|
|
19
20
|
};
|
|
20
21
|
|
|
21
|
-
type ValidatedHandler = (ctx: HandlerContext, params: unknown) => Promise<
|
|
22
|
+
type ValidatedHandler = (ctx: HandlerContext, params: unknown) => Promise<Result>;
|
|
22
23
|
type WidgetTarget = { testing: TestingModule; widget: Gtk.Widget };
|
|
23
24
|
type WidgetParams = { widgetId?: string | undefined };
|
|
24
|
-
type TargetedHandler<Params> = (ctx: HandlerContext, target: WidgetTarget, params: Params) => Promise<
|
|
25
|
+
type TargetedHandler<Params> = (ctx: HandlerContext, target: WidgetTarget, params: Params) => Promise<Result>;
|
|
25
26
|
type QueryParams = ServerRequestParams<"widget.query">;
|
|
26
27
|
type QueryBy = QueryParams["by"];
|
|
27
28
|
type QueryRunner = (testing: TestingModule, app: Gtk.Application, params: QueryParams) => Promise<Gtk.Widget[]>;
|
|
@@ -110,7 +111,7 @@ const SEARCHED_BY: Record<QueryBy, string> = {
|
|
|
110
111
|
|
|
111
112
|
function validated<Params>(
|
|
112
113
|
schema: ParamsSchema<Params>,
|
|
113
|
-
handler: (ctx: HandlerContext, params: Params) => Promise<
|
|
114
|
+
handler: (ctx: HandlerContext, params: Params) => Promise<Result>,
|
|
114
115
|
): ValidatedHandler {
|
|
115
116
|
return (ctx, params) => {
|
|
116
117
|
const parsed = schema.safeParse(params ?? {});
|
|
@@ -229,7 +230,7 @@ const emptyQueryHint = (params: QueryParams): string =>
|
|
|
229
230
|
"Call gtkx_get_widget_tree to see what is mounted; by:\"name\" is the widest match, and by:\"role\" accepts " +
|
|
230
231
|
"options.name to match the accessible name of a known role.";
|
|
231
232
|
|
|
232
|
-
async function handleQuery({ app, registry }: HandlerContext, params: QueryParams): Promise<
|
|
233
|
+
async function handleQuery({ app, registry }: HandlerContext, params: QueryParams): Promise<Result> {
|
|
233
234
|
const testing = await loadTestingModule();
|
|
234
235
|
const widgets = await QUERY_RUNNERS[params.by](testing, app, params);
|
|
235
236
|
const resolveId = (widget: Gtk.Widget): string => registry.getOrCreateId(widget);
|
|
@@ -254,7 +255,7 @@ const defaultScreenshotTarget = (registry: WidgetRegistry): Gtk.Widget => {
|
|
|
254
255
|
async function handleScreenshot(
|
|
255
256
|
{ registry }: HandlerContext,
|
|
256
257
|
params: ServerRequestParams<"widget.screenshot">,
|
|
257
|
-
): Promise<
|
|
258
|
+
): Promise<Result> {
|
|
258
259
|
const testing = await loadTestingModule();
|
|
259
260
|
const target = params.windowId ? requireWidget(registry, params.windowId) : defaultScreenshotTarget(registry);
|
|
260
261
|
const result = await testing.screenshot(target, { path: params.path });
|
|
@@ -269,7 +270,7 @@ async function handleScreenshot(
|
|
|
269
270
|
const isServerInitiatedMethod = (method: string): method is ServerInitiatedMethod =>
|
|
270
271
|
Object.hasOwn(ServerRequestParamsSchemas, method);
|
|
271
272
|
|
|
272
|
-
const dispatch = async (method: string, params: unknown, ctx: HandlerContext): Promise<
|
|
273
|
+
const dispatch = async (method: string, params: unknown, ctx: HandlerContext): Promise<Result> => {
|
|
273
274
|
if (!isServerInitiatedMethod(method)) {
|
|
274
275
|
throw methodNotFoundError(method);
|
|
275
276
|
}
|
package/src/settings/render.ts
CHANGED
|
@@ -13,6 +13,10 @@ type EnvAssetDeclarations = {
|
|
|
13
13
|
resources: string[];
|
|
14
14
|
};
|
|
15
15
|
|
|
16
|
+
type EnvModuleOptions = {
|
|
17
|
+
references?: string[];
|
|
18
|
+
};
|
|
19
|
+
|
|
16
20
|
const GTKX_ENV_MODULE_HEADER = `/**
|
|
17
21
|
* Project import declarations generated by GTKX. Regenerated by
|
|
18
22
|
* \`gtkx codegen\`, \`gtkx dev\`, and \`gtkx build\`; do not edit.
|
|
@@ -226,6 +230,7 @@ const renderBlockedAssetModule = (specifier: string): string => [
|
|
|
226
230
|
const renderEnvModule = (
|
|
227
231
|
files: ParsedSchemaFile[],
|
|
228
232
|
assetDeclarations?: EnvAssetDeclarations,
|
|
233
|
+
options: EnvModuleOptions = {},
|
|
229
234
|
): string => {
|
|
230
235
|
const assets = assetDeclarations ?? { blocked: [], icons: [], legacy: [], resources: [] };
|
|
231
236
|
const usedNames: Set<string> = new Set();
|
|
@@ -238,8 +243,17 @@ const renderEnvModule = (
|
|
|
238
243
|
const icons = assets.icons.map((specifier) => renderIconModule(specifier));
|
|
239
244
|
const legacy = assets.legacy.map((specifier) => renderLegacyAssetModule(specifier));
|
|
240
245
|
const resources = assets.resources.map((specifier) => renderResourceModule(specifier));
|
|
241
|
-
|
|
242
|
-
|
|
246
|
+
const references = (options.references ?? []).map((path) => `/// <reference path=${JSON.stringify(path)} />`);
|
|
247
|
+
|
|
248
|
+
return `${[
|
|
249
|
+
GTKX_ENV_MODULE_HEADER,
|
|
250
|
+
...references,
|
|
251
|
+
...blocked,
|
|
252
|
+
...icons,
|
|
253
|
+
...legacy,
|
|
254
|
+
...resources,
|
|
255
|
+
...schemas,
|
|
256
|
+
].join("\n\n")}\n`;
|
|
243
257
|
};
|
|
244
258
|
|
|
245
259
|
export { renderRuntimeModule, renderEnvModule };
|
package/src/settings/schema.ts
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
} from "node:fs";
|
|
14
14
|
import { tmpdir } from "node:os";
|
|
15
15
|
import { basename, dirname, isAbsolute, join, relative, resolve, sep } from "node:path";
|
|
16
|
+
import { I18N_TYPES_FILENAME, i18nTypesPath } from "../i18n/types.js";
|
|
16
17
|
import { DATA_IMPORT_PREFIX } from "../internal/data-dir.js";
|
|
17
18
|
import { discoverSourceImports, type SourceImport } from "../internal/source-imports.js";
|
|
18
19
|
import { removeTempDir } from "../internal/staging-dir.js";
|
|
@@ -323,7 +324,8 @@ const emitSchemaEnv = (
|
|
|
323
324
|
assertUniqueSchemaBasenames(importedFiles);
|
|
324
325
|
const imported = parseProjectSchemas(importedFiles, getRelativeModuleSpecifier);
|
|
325
326
|
const assets = findAssetModuleSpecifiers(imports, isV2ResourceImports);
|
|
326
|
-
const
|
|
327
|
+
const references = existsSync(i18nTypesPath(rootDir)) ? [`./${I18N_TYPES_FILENAME}`] : [];
|
|
328
|
+
const content = renderEnvModule([...legacy, ...imported], assets, { references });
|
|
327
329
|
const path = schemaEnvPath(rootDir);
|
|
328
330
|
const isWritten = didWriteChanges(path, content);
|
|
329
331
|
|
|
@@ -0,0 +1,486 @@
|
|
|
1
|
+
import type { ConfigLoader } from "@gtkx/config";
|
|
2
|
+
import type { Plugin, UserConfig } from "vite";
|
|
3
|
+
import { type GeneratedElement, readGeneratedElements } from "@gtkx/codegen";
|
|
4
|
+
import { createConfigLoader } from "@gtkx/config/internal";
|
|
5
|
+
import { createRequire } from "node:module";
|
|
6
|
+
import { dirname, join } from "node:path";
|
|
7
|
+
import { parseSync } from "vite";
|
|
8
|
+
import { sourceLanguage } from "../internal/source-imports.js";
|
|
9
|
+
import { stripQuery } from "./strip-query.js";
|
|
10
|
+
|
|
11
|
+
type AstNode = {
|
|
12
|
+
type: string;
|
|
13
|
+
start: number;
|
|
14
|
+
end: number;
|
|
15
|
+
} & Record<string, unknown>;
|
|
16
|
+
|
|
17
|
+
type Edit = {
|
|
18
|
+
start: number;
|
|
19
|
+
end: number;
|
|
20
|
+
replacement: string;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
type Rewrite = {
|
|
24
|
+
edits: Edit[];
|
|
25
|
+
hasMemberEdits: boolean;
|
|
26
|
+
hasCallEdits: boolean;
|
|
27
|
+
hasDynamicUses: boolean;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
type RewrittenModule = { code: string; hasDynamicUses: boolean; hasMemberEdits: boolean };
|
|
31
|
+
|
|
32
|
+
type PluginState = {
|
|
33
|
+
isEnabled: boolean;
|
|
34
|
+
isBuild: boolean;
|
|
35
|
+
root: string;
|
|
36
|
+
elements: Map<string, GeneratedElement> | null;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const ANIMATED_PACKAGE = "@gtkx/animated";
|
|
40
|
+
const INTERNAL_SPECIFIER = "@gtkx/animated/internal";
|
|
41
|
+
const VIRTUAL_ID = "virtual:gtkx-animated";
|
|
42
|
+
const RESOLVED_VIRTUAL_ID = `\0${VIRTUAL_ID}`;
|
|
43
|
+
const NAMESPACE_HELPER = "__gtkxAnimated";
|
|
44
|
+
const CALL_HELPER = "__gtkxWithAnimated";
|
|
45
|
+
|
|
46
|
+
const BINDING_PARENT_KEYS: Record<string, string> = {
|
|
47
|
+
VariableDeclarator: "id",
|
|
48
|
+
ImportSpecifier: "local",
|
|
49
|
+
ImportDefaultSpecifier: "local",
|
|
50
|
+
ImportNamespaceSpecifier: "local",
|
|
51
|
+
FunctionDeclaration: "id",
|
|
52
|
+
ClassDeclaration: "id",
|
|
53
|
+
CatchClause: "param",
|
|
54
|
+
AssignmentPattern: "left",
|
|
55
|
+
RestElement: "argument",
|
|
56
|
+
Property: "value",
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const BINDING_PARENT_ARRAY_KEYS: Record<string, string> = {
|
|
60
|
+
ArrowFunctionExpression: "params",
|
|
61
|
+
FunctionDeclaration: "params",
|
|
62
|
+
FunctionExpression: "params",
|
|
63
|
+
ArrayPattern: "elements",
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const isAstNode = (value: unknown): value is AstNode =>
|
|
67
|
+
typeof value === "object" &&
|
|
68
|
+
value !== null &&
|
|
69
|
+
typeof (value as AstNode).type === "string" &&
|
|
70
|
+
typeof (value as AstNode).start === "number" &&
|
|
71
|
+
typeof (value as AstNode).end === "number";
|
|
72
|
+
|
|
73
|
+
const walkChildren = (node: AstNode, visit: (node: AstNode, parent: AstNode | null) => void): void => {
|
|
74
|
+
for (const [key, child] of Object.entries(node)) {
|
|
75
|
+
if (key !== "type") {
|
|
76
|
+
walk(child, node, visit);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const walk = (value: unknown, parent: AstNode | null, visit: (node: AstNode, parent: AstNode | null) => void): void => {
|
|
82
|
+
if (Array.isArray(value)) {
|
|
83
|
+
for (const item of value) {
|
|
84
|
+
walk(item, parent, visit);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (!isAstNode(value)) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
visit(value, parent);
|
|
95
|
+
walkChildren(value, visit);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const identifierName = (node: unknown): string | undefined => {
|
|
99
|
+
if (!isAstNode(node)) {
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (node.type !== "Identifier" && node.type !== "JSXIdentifier") {
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return typeof node.name === "string" ? node.name : undefined;
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
const isValueImportSpecifier = (specifier: unknown): specifier is AstNode =>
|
|
111
|
+
isAstNode(specifier) && specifier.type === "ImportSpecifier" && specifier.importKind !== "type";
|
|
112
|
+
|
|
113
|
+
const animatedSpecifierLocal = (node: AstNode): string | undefined => {
|
|
114
|
+
const specifiers = (node.specifiers as unknown[] | undefined) ?? [];
|
|
115
|
+
let local: string | undefined;
|
|
116
|
+
|
|
117
|
+
for (const specifier of specifiers) {
|
|
118
|
+
if (isValueImportSpecifier(specifier) && identifierName(specifier.imported) === "animated") {
|
|
119
|
+
local ??= identifierName(specifier.local);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
return local;
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
const animatedLocalName = (program: AstNode): string | undefined => {
|
|
127
|
+
let local: string | undefined;
|
|
128
|
+
|
|
129
|
+
walk(program, null, (node) => {
|
|
130
|
+
if (node.type !== "ImportDeclaration" || (node.source as AstNode | undefined)?.value !== ANIMATED_PACKAGE) {
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (node.importKind === "type") {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
local ??= animatedSpecifierLocal(node);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
return local;
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const isBindingSlot = (node: AstNode, parent: AstNode): boolean => {
|
|
145
|
+
const key = BINDING_PARENT_KEYS[parent.type];
|
|
146
|
+
|
|
147
|
+
return key !== undefined && parent[key] === node;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const isBindingListEntry = (node: AstNode, parent: AstNode): boolean => {
|
|
151
|
+
const arrayKey = BINDING_PARENT_ARRAY_KEYS[parent.type];
|
|
152
|
+
const entries = arrayKey === undefined ? undefined : parent[arrayKey];
|
|
153
|
+
|
|
154
|
+
return Array.isArray(entries) && entries.includes(node);
|
|
155
|
+
};
|
|
156
|
+
|
|
157
|
+
const isBindingParent = (node: AstNode, parent: AstNode): boolean =>
|
|
158
|
+
isBindingSlot(node, parent) || isBindingListEntry(node, parent);
|
|
159
|
+
|
|
160
|
+
const isBindingNamed = (node: AstNode, parent: AstNode, name: string): boolean =>
|
|
161
|
+
node.type === "Identifier" &&
|
|
162
|
+
node.name === name &&
|
|
163
|
+
isBindingParent(node, parent);
|
|
164
|
+
|
|
165
|
+
const countDeclarations = (program: AstNode, name: string): number => {
|
|
166
|
+
const spans: Set<number> = new Set();
|
|
167
|
+
|
|
168
|
+
walk(program, null, (node, parent) => {
|
|
169
|
+
if (parent !== null && isBindingNamed(node, parent, name)) {
|
|
170
|
+
spans.add(node.start);
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
return spans.size;
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
const isRewritableJsxMember = (node: AstNode, local: string): boolean =>
|
|
178
|
+
node.type === "JSXMemberExpression" && identifierName(node.object) === local;
|
|
179
|
+
|
|
180
|
+
const isRewritableObjectMember = (node: AstNode, local: string): boolean =>
|
|
181
|
+
node.type === "MemberExpression" &&
|
|
182
|
+
node.computed !== true &&
|
|
183
|
+
node.optional !== true &&
|
|
184
|
+
identifierName(node.object) === local;
|
|
185
|
+
|
|
186
|
+
const isForbiddenTarget = (node: AstNode, parent: AstNode | null): boolean =>
|
|
187
|
+
parent !== null &&
|
|
188
|
+
((parent.type === "AssignmentExpression" && parent.left === node) ||
|
|
189
|
+
(parent.type === "UpdateExpression" && parent.argument === node) ||
|
|
190
|
+
(parent.type === "UnaryExpression" && parent.operator === "delete" && parent.argument === node));
|
|
191
|
+
|
|
192
|
+
const isRewritableMember = (node: AstNode, parent: AstNode | null, local: string): boolean => {
|
|
193
|
+
if (isRewritableJsxMember(node, local)) {
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return isRewritableObjectMember(node, local) && !isForbiddenTarget(node, parent);
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
const collectMemberEdit = (
|
|
201
|
+
rewrite: Rewrite,
|
|
202
|
+
claimed: Set<number>,
|
|
203
|
+
node: AstNode,
|
|
204
|
+
elements: Map<string, GeneratedElement>,
|
|
205
|
+
): void => {
|
|
206
|
+
const property = identifierName(node.property);
|
|
207
|
+
|
|
208
|
+
if (property !== undefined && elements.get(property)?.isMountable === true) {
|
|
209
|
+
const object = node.object as AstNode;
|
|
210
|
+
rewrite.edits.push({ start: object.start, end: object.end, replacement: NAMESPACE_HELPER });
|
|
211
|
+
rewrite.hasMemberEdits = true;
|
|
212
|
+
claimed.add(object.start);
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
const collectCallEdit = (rewrite: Rewrite, claimed: Set<number>, node: AstNode): void => {
|
|
217
|
+
const callee = node.callee as AstNode;
|
|
218
|
+
rewrite.edits.push({ start: callee.start, end: callee.end, replacement: CALL_HELPER });
|
|
219
|
+
rewrite.hasCallEdits = true;
|
|
220
|
+
claimed.add(callee.start);
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
const isPropertyReference = (node: AstNode, parent: AstNode | null): boolean =>
|
|
224
|
+
parent?.type === "ImportSpecifier" ||
|
|
225
|
+
(parent?.type === "MemberExpression" && parent.property === node) ||
|
|
226
|
+
(parent?.type === "JSXMemberExpression" && parent.property === node);
|
|
227
|
+
|
|
228
|
+
const hasDynamicUse = (program: AstNode, local: string, claimed: Set<number>): boolean => {
|
|
229
|
+
let isFound = false;
|
|
230
|
+
|
|
231
|
+
walk(program, null, (node, parent) => {
|
|
232
|
+
if (identifierName(node) === local && !claimed.has(node.start) && !isPropertyReference(node, parent)) {
|
|
233
|
+
isFound = true;
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
return isFound;
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
const collectRewrite = (program: AstNode, local: string, elements: Map<string, GeneratedElement>): Rewrite => {
|
|
241
|
+
const rewrite: Rewrite = { edits: [], hasMemberEdits: false, hasCallEdits: false, hasDynamicUses: false };
|
|
242
|
+
const claimed: Set<number> = new Set();
|
|
243
|
+
|
|
244
|
+
walk(program, null, (node, parent) => {
|
|
245
|
+
if (isRewritableMember(node, parent, local)) {
|
|
246
|
+
collectMemberEdit(rewrite, claimed, node, elements);
|
|
247
|
+
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (node.type === "CallExpression" && identifierName(node.callee) === local) {
|
|
252
|
+
collectCallEdit(rewrite, claimed, node);
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
rewrite.hasDynamicUses = hasDynamicUse(program, local, claimed);
|
|
257
|
+
|
|
258
|
+
return rewrite;
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
const lastImportEnd = (program: AstNode): number => {
|
|
262
|
+
const statements = (program.body as unknown[] | undefined) ?? [];
|
|
263
|
+
let end = 0;
|
|
264
|
+
|
|
265
|
+
for (const statement of statements) {
|
|
266
|
+
if (isAstNode(statement) && statement.type === "ImportDeclaration") {
|
|
267
|
+
end = Math.max(end, statement.end);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
return end;
|
|
272
|
+
};
|
|
273
|
+
|
|
274
|
+
const helperImports = (rewrite: Rewrite): string => {
|
|
275
|
+
const lines: string[] = [];
|
|
276
|
+
|
|
277
|
+
if (rewrite.hasMemberEdits) {
|
|
278
|
+
lines.push(`import * as ${NAMESPACE_HELPER} from ${JSON.stringify(VIRTUAL_ID)};`);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (rewrite.hasCallEdits) {
|
|
282
|
+
lines.push(`import { withAnimated as ${CALL_HELPER} } from ${JSON.stringify(INTERNAL_SPECIFIER)};`);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
return lines.join("\n");
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
const applyEdits = (code: string, edits: Edit[]): string => {
|
|
289
|
+
const ordered = edits.toSorted((a, b) => b.start - a.start);
|
|
290
|
+
let result = code;
|
|
291
|
+
|
|
292
|
+
for (const edit of ordered) {
|
|
293
|
+
result = `${result.slice(0, edit.start)}${edit.replacement}${result.slice(edit.end)}`;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
return result;
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
const parseProgram = (path: string, code: string): AstNode | undefined => {
|
|
300
|
+
const lang = sourceLanguage(path);
|
|
301
|
+
|
|
302
|
+
if (lang === undefined) {
|
|
303
|
+
return undefined;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const parsed = parseSync(path, code, { lang });
|
|
307
|
+
|
|
308
|
+
return parsed.errors.length > 0 ? undefined : (parsed.program as unknown as AstNode);
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
const unchangedRewrite = (code: string, rewrite: Rewrite): RewrittenModule | undefined =>
|
|
312
|
+
rewrite.hasDynamicUses ? { code, hasDynamicUses: true, hasMemberEdits: false } : undefined;
|
|
313
|
+
|
|
314
|
+
const hasHelperCollision = (code: string): boolean =>
|
|
315
|
+
code.includes(NAMESPACE_HELPER) || code.includes(CALL_HELPER);
|
|
316
|
+
|
|
317
|
+
const rewriteModule = (
|
|
318
|
+
code: string,
|
|
319
|
+
id: string,
|
|
320
|
+
elements: Map<string, GeneratedElement>,
|
|
321
|
+
): RewrittenModule | undefined => {
|
|
322
|
+
if (hasHelperCollision(code)) {
|
|
323
|
+
return undefined;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const program = parseProgram(stripQuery(id), code);
|
|
327
|
+
|
|
328
|
+
if (program === undefined) {
|
|
329
|
+
return undefined;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const local = animatedLocalName(program);
|
|
333
|
+
|
|
334
|
+
if (local === undefined || countDeclarations(program, local) > 1) {
|
|
335
|
+
return undefined;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
const rewrite = collectRewrite(program, local, elements);
|
|
339
|
+
|
|
340
|
+
if (rewrite.edits.length === 0) {
|
|
341
|
+
return unchangedRewrite(code, rewrite);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const insertAt = lastImportEnd(program);
|
|
345
|
+
const helperEdit: Edit = { start: insertAt, end: insertAt, replacement: `\n${helperImports(rewrite)}` };
|
|
346
|
+
const withHelpers = applyEdits(code, [...rewrite.edits, helperEdit]);
|
|
347
|
+
|
|
348
|
+
return { code: withHelpers, hasDynamicUses: rewrite.hasDynamicUses, hasMemberEdits: rewrite.hasMemberEdits };
|
|
349
|
+
};
|
|
350
|
+
|
|
351
|
+
const renderVirtualModule = (elements: Map<string, GeneratedElement>): string => {
|
|
352
|
+
const mountable = elements.values().filter((element) => element.isMountable).toArray();
|
|
353
|
+
const imports = mountable.map((element) => `${element.glibName} as $${element.glibName}`);
|
|
354
|
+
|
|
355
|
+
const consts = mountable.map(
|
|
356
|
+
(element) => `export const ${element.glibName} = /* @__PURE__ */ withAnimated($${element.glibName});`,
|
|
357
|
+
);
|
|
358
|
+
|
|
359
|
+
return (
|
|
360
|
+
`import { ${imports.join(", ")} } from "@gtkx/jsx";\n` +
|
|
361
|
+
`import { withAnimated } from ${JSON.stringify(INTERNAL_SPECIFIER)};\n` +
|
|
362
|
+
`${consts.join("\n")}\n`
|
|
363
|
+
);
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
const loadElements = (state: PluginState): Map<string, GeneratedElement> | null => {
|
|
367
|
+
if (state.elements !== null) {
|
|
368
|
+
return state.elements;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
try {
|
|
372
|
+
const requireFromRoot = createRequire(join(state.root, "package.json"));
|
|
373
|
+
const jsxStoreDir = dirname(requireFromRoot.resolve("@gtkx/jsx/package.json"));
|
|
374
|
+
state.elements = new Map(readGeneratedElements(jsxStoreDir).map((element) => [element.glibName, element]));
|
|
375
|
+
} catch {
|
|
376
|
+
return null;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
return state.elements;
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
const resolveVirtualId = (state: PluginState, source: string): string | undefined => {
|
|
383
|
+
if (source === VIRTUAL_ID && state.isEnabled) {
|
|
384
|
+
return RESOLVED_VIRTUAL_ID;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
return undefined;
|
|
388
|
+
};
|
|
389
|
+
|
|
390
|
+
const loadVirtualModule = (state: PluginState, id: string): string | undefined => {
|
|
391
|
+
if (id !== RESOLVED_VIRTUAL_ID || !state.isEnabled) {
|
|
392
|
+
return undefined;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
const elements = loadElements(state);
|
|
396
|
+
|
|
397
|
+
return elements === null ? undefined : renderVirtualModule(elements);
|
|
398
|
+
};
|
|
399
|
+
|
|
400
|
+
const dynamicUseWarning = (id: string, hasDynamicUses: boolean): string[] =>
|
|
401
|
+
hasDynamicUses
|
|
402
|
+
? [
|
|
403
|
+
`${stripQuery(id)} uses the animated binding dynamically, which keeps the whole generated ` +
|
|
404
|
+
"widget namespace in the bundle; use animated(Component) calls to let unused widgets be dropped",
|
|
405
|
+
]
|
|
406
|
+
: [];
|
|
407
|
+
|
|
408
|
+
const memberUseWarning = (id: string, hasMemberEdits: boolean): string[] =>
|
|
409
|
+
hasMemberEdits
|
|
410
|
+
? [
|
|
411
|
+
`${stripQuery(id)} reads widget components off the animated binding, which GTKX 2.0 removes; ` +
|
|
412
|
+
"import the component and call animated(Component) instead",
|
|
413
|
+
]
|
|
414
|
+
: [];
|
|
415
|
+
|
|
416
|
+
const warningsFor = (state: PluginState, id: string, rewritten: RewrittenModule): string[] =>
|
|
417
|
+
state.isBuild
|
|
418
|
+
? [
|
|
419
|
+
...dynamicUseWarning(id, rewritten.hasDynamicUses),
|
|
420
|
+
...memberUseWarning(id, rewritten.hasMemberEdits),
|
|
421
|
+
]
|
|
422
|
+
: [];
|
|
423
|
+
|
|
424
|
+
const transformCode = (
|
|
425
|
+
state: PluginState,
|
|
426
|
+
code: string,
|
|
427
|
+
id: string,
|
|
428
|
+
): { code: string; warnings: string[] } | undefined => {
|
|
429
|
+
if (!state.isEnabled || id.startsWith("\0") || !code.includes(ANIMATED_PACKAGE)) {
|
|
430
|
+
return undefined;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
const elements = loadElements(state);
|
|
434
|
+
|
|
435
|
+
if (elements === null) {
|
|
436
|
+
return undefined;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
const rewritten = rewriteModule(code, id, elements);
|
|
440
|
+
|
|
441
|
+
if (rewritten === undefined) {
|
|
442
|
+
return undefined;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
return { code: rewritten.code, warnings: warningsFor(state, id, rewritten) };
|
|
446
|
+
};
|
|
447
|
+
|
|
448
|
+
function gtkxAnimated(loadConfig: ConfigLoader = createConfigLoader()): Plugin {
|
|
449
|
+
const state: PluginState = { isEnabled: false, isBuild: false, root: process.cwd(), elements: null };
|
|
450
|
+
|
|
451
|
+
return {
|
|
452
|
+
name: "gtkx:animated",
|
|
453
|
+
enforce: "pre",
|
|
454
|
+
|
|
455
|
+
async config(config: UserConfig) {
|
|
456
|
+
const loaded = await loadConfig.load(config.root ?? process.cwd());
|
|
457
|
+
state.isEnabled = loaded.config.future?.v2TreeShaking === true;
|
|
458
|
+
},
|
|
459
|
+
|
|
460
|
+
configResolved(config) {
|
|
461
|
+
state.root = config.root;
|
|
462
|
+
state.isBuild = config.command === "build";
|
|
463
|
+
},
|
|
464
|
+
|
|
465
|
+
resolveId(source) {
|
|
466
|
+
return resolveVirtualId(state, source);
|
|
467
|
+
},
|
|
468
|
+
|
|
469
|
+
load(id) {
|
|
470
|
+
return loadVirtualModule(state, id);
|
|
471
|
+
},
|
|
472
|
+
|
|
473
|
+
transform(code, id) {
|
|
474
|
+
const result = transformCode(state, code, id);
|
|
475
|
+
const warnings = result?.warnings ?? [];
|
|
476
|
+
|
|
477
|
+
for (const warning of warnings) {
|
|
478
|
+
this.warn(warning);
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
return result?.code;
|
|
482
|
+
},
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
export { gtkxAnimated };
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { ConfigLoader } from "@gtkx/config";
|
|
2
|
-
import type { ParseResult,
|
|
3
|
-
import { createConfigLoader } from "@gtkx/config/internal";
|
|
4
|
-
import { extname } from "node:path";
|
|
2
|
+
import type { ParseResult, Plugin, UserConfig } from "vite";
|
|
3
|
+
import { createConfigLoader, resolveFuture } from "@gtkx/config/internal";
|
|
5
4
|
import { parseSync } from "vite";
|
|
5
|
+
import { sourceLanguage } from "../internal/source-imports.js";
|
|
6
6
|
import { ASSET_MENTION_RE } from "./asset-extensions.js";
|
|
7
7
|
import {
|
|
8
8
|
DATA_PREFIX,
|
|
@@ -15,7 +15,6 @@ import {
|
|
|
15
15
|
import { RESOURCE_PATH_EXPORT } from "./resource-shared.js";
|
|
16
16
|
import { stripQuery } from "./strip-query.js";
|
|
17
17
|
|
|
18
|
-
type SourceLanguage = NonNullable<ParserOptions["lang"]>;
|
|
19
18
|
type ParsedModule = ParseResult["module"];
|
|
20
19
|
type ImportStatement = ParsedModule["staticImports"][number];
|
|
21
20
|
type ExportStatement = ParsedModule["staticExports"][number];
|
|
@@ -24,17 +23,6 @@ type BindingEntry = { importName: { name: string | null }; isType: boolean };
|
|
|
24
23
|
type NamedBinding = { name: string; source: string };
|
|
25
24
|
type PluginState = { isV2: boolean };
|
|
26
25
|
|
|
27
|
-
const SOURCE_LANGUAGES: Map<string, SourceLanguage> = new Map([
|
|
28
|
-
[".cjs", "jsx"],
|
|
29
|
-
[".cts", "ts"],
|
|
30
|
-
[".js", "jsx"],
|
|
31
|
-
[".jsx", "jsx"],
|
|
32
|
-
[".mjs", "jsx"],
|
|
33
|
-
[".mts", "ts"],
|
|
34
|
-
[".ts", "ts"],
|
|
35
|
-
[".tsx", "tsx"],
|
|
36
|
-
]);
|
|
37
|
-
|
|
38
26
|
const NODE_MODULES = /(?:^|\/)node_modules\//;
|
|
39
27
|
const VIRTUAL_PREFIX = "\0";
|
|
40
28
|
const DEFAULT_BINDING = "default";
|
|
@@ -135,7 +123,7 @@ const hasCheckedAssetMention = (code: string): boolean => ASSET_MENTION_RE.test(
|
|
|
135
123
|
|
|
136
124
|
const checkAssetImports = (code: string, id: string, isV2: boolean): void => {
|
|
137
125
|
const path = stripQuery(id);
|
|
138
|
-
const lang =
|
|
126
|
+
const lang = sourceLanguage(path);
|
|
139
127
|
|
|
140
128
|
if (lang === undefined || !isCheckedSource(path) || !hasCheckedAssetMention(code)) {
|
|
141
129
|
return;
|
|
@@ -163,7 +151,7 @@ function gtkxAssetImports(loadConfig: ConfigLoader = createConfigLoader()): Plug
|
|
|
163
151
|
|
|
164
152
|
async config(config: UserConfig) {
|
|
165
153
|
const loaded = await loadConfig.load(config.root ?? process.cwd());
|
|
166
|
-
state.isV2 = loaded.config.future
|
|
154
|
+
state.isV2 = resolveFuture(loaded.config.future).isResourceImported;
|
|
167
155
|
},
|
|
168
156
|
|
|
169
157
|
transform(code, id) {
|