diff --git a/.changeset/tidy-teachers-bathe.md b/.changeset/tidy-teachers-bathe.md new file mode 100644 index 0000000..d490559 --- /dev/null +++ b/.changeset/tidy-teachers-bathe.md @@ -0,0 +1,5 @@ +--- +"arc-vite": patch +--- + +Reverts virtual adaptive module support for now since it was not properly working and dramatically increased the complexity of this module and caused a few regressions that were difficult to resolve. diff --git a/src/index.ts b/src/index.ts index 7655b14..a89cacf 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,7 +5,6 @@ import type { Plugin } from "vite"; import { pluginBuildSSR } from "./plugins/build-ssr"; import { pluginBuildWeb } from "./plugins/build-web"; import { pluginServe } from "./plugins/serve"; -import type { FlagSet } from "./utils/flags"; import { type Options, getInternalPluginOptions } from "./utils/options"; export { createFlagSets, hasFlags } from "./utils/flags"; @@ -30,13 +29,3 @@ declare module "arc-server" { body?: string; }; } - -declare module "rollup" { - interface CustomPluginOptions { - arcSourceId?: string; - arcSourceCode?: string; - arcScanIds?: string[]; - arcFlagSet?: FlagSet; - arcFS?: typeof import("fs"); - } -} diff --git a/src/plugins/build-ssr.ts b/src/plugins/build-ssr.ts index ed10a01..b45865f 100644 --- a/src/plugins/build-ssr.ts +++ b/src/plugins/build-ssr.ts @@ -1,22 +1,18 @@ import path from "path"; -import type { Plugin } from "vite"; -import { getArcFS } from "../utils/arc-fs"; -import { ensureArcPluginIsFirst } from "../utils/ensure-arc-plugin-is-first"; -import { decodeFileName, encodeFileName } from "../utils/filename-encoding"; +import type { Plugin, Rollup } from "vite"; +import { isAssetFile, isGlobalCSSFile } from "../utils/file-types"; import { indexToId } from "../utils/index-to-id"; -import { isCssFile } from "../utils/is-css-file"; -import { type Matches, clearCache } from "../utils/matches"; +import { clearCache, getMatches, type Matches } from "../utils/matches"; import { type InternalPluginOptions } from "../utils/options"; import { toPosix } from "../utils/to-posix"; -import { - decodeArcVirtualMatch, - getVirtualMatches, - isArcVirtualMatch, -} from "../utils/virtual-matches"; +interface ProxyMeta { + resolved: Rollup.ResolvedId; + matches: Matches; +} const virtualArcServerModuleId = "\0arc-server-virtual"; const arcPrefix = "\0arc-"; -const arcSuffix = ".mjs"; +const arcJsSuffix = ".mjs"; const arcProxyPrefix = `${arcPrefix}proxy:`; // TODO: with some tweaks this plugin might work in a test env. @@ -26,8 +22,9 @@ export function pluginBuildSSR({ flagSets, forceFlagSet, }: InternalPluginOptions): Plugin { - const adaptiveMatchesForId = new Map(); let root: string; + let proxyModuleId = 0; + let metaForProxy = new Map(); return { name: "arc-vite:build-ssr", enforce: "pre", @@ -41,9 +38,6 @@ export function pluginBuildSSR({ apply(config, { command }) { return command === "build" && !!config.build?.ssr; }, - config(config) { - ensureArcPluginIsFirst(config.plugins!); - }, configResolved(config) { root = config.root; }, @@ -63,14 +57,6 @@ export function pluginBuildSSR({ } if (isArcId(importer)) { - if (isArcVirtualMatch(importer)) { - return this.resolve( - source, - this.getModuleInfo(importer)?.meta.arcSourceId, - options, - ); - } - return source; } @@ -79,15 +65,12 @@ export function pluginBuildSSR({ skipSelf: true, }); if (resolved && !resolved.external) { - const matches = await getVirtualMatches(this, flagSets, resolved); + const { id } = resolved; + const matches = getMatches(id, flagSets); if (matches) { - const { id } = resolved; - if (!this.getModuleInfo(id)?.ast) { - await this.load(resolved); - } - - adaptiveMatchesForId.set(id, matches); - return { id: encodeArcProxyId(id) }; + const proxyId = nextProxyId(); + metaForProxy.set(proxyId, { resolved, matches }); + return proxyId; } } @@ -132,106 +115,96 @@ function partsToString(parts, base, injectAttrs) { } if (isArcProxyId(id)) { - id = decodeArcProxyId(id); - const adaptiveMatches = adaptiveMatchesForId.get(id); - if (adaptiveMatches) { - if (isCssFile(id)) { - let code = ""; - for (const { value } of adaptiveMatches.alternates) { - code += `import ${JSON.stringify(value)};\n`; - } - - code += `import ${JSON.stringify(adaptiveMatches.default)};\n`; + const { resolved, matches } = metaForProxy.get(id)!; + let code = ""; - return { - code, - moduleSideEffects: "no-treeshake", - }; + if (isGlobalCSSFile(resolved.id)) { + for (const { value } of matches.alternates) { + code += `import ${JSON.stringify(value)};\n`; } - const info = this.getModuleInfo(id); - if (info) { - let code = ""; - let matchCode = ""; - let matchCodeSep = ""; - let i = 0; + code += `import ${JSON.stringify(matches.default)};\n`; + + return { + code, + moduleSideEffects: "no-treeshake", + }; + } - for (const { flags, value } of adaptiveMatches.alternates) { - const adaptedImportId = `_${indexToId(i++)}`; - code += `import * as ${adaptedImportId} from ${JSON.stringify( - value, - )};\n`; + let matchCode = ""; + let matchCodeSep = ""; + let i = 0; - matchCode += - matchCodeSep + - flags.map((flag) => `f.${flag}`).join("&&") + - "?" + - adaptedImportId; + for (const { flags, value } of matches.alternates) { + const adaptedImportId = `_${indexToId(i++)}`; + code += `import * as ${adaptedImportId} from ${JSON.stringify( + value, + )};\n`; - matchCodeSep = ":"; - } + matchCode += + matchCodeSep + + flags.map((flag) => `f.${flag}`).join("&&") + + "?" + + adaptedImportId; + + matchCodeSep = ":"; + } - const defaultId = `_${indexToId(i)}`; - code += `import * as ${defaultId} from ${JSON.stringify( - adaptiveMatches.default, - )};\n`; - matchCode += `:${defaultId}`; + const defaultId = `_${indexToId(i)}`; + code += `import * as ${defaultId} from ${JSON.stringify( + matches.default, + )};\n`; + matchCode += `:${defaultId}`; + + let syntheticNamedExports: string | boolean = false; + let hasNamedExports = false; + let hasDefaultExport = false; + + if (isAssetFile(resolved.id)) { + hasDefaultExport = true; + } else { + let info = this.getModuleInfo(resolved.id); + if (!info?.ast) { + info = await this.load(resolved); + } - let syntheticNamedExports: string | boolean = false; - if (info.exports?.length) { - const hasNamedExports = - info.exports.length >= (info.hasDefaultExport ? 2 : 1); + if (info.exports) { + hasDefaultExport = info.hasDefaultExport === true; + hasNamedExports = info.exports.length >= (hasDefaultExport ? 2 : 1); + } + } - if (hasNamedExports || info.hasDefaultExport) { - const proxyCode = `/*@__PURE__*/createAdaptiveProxy({default:${defaultId},match(f){return ${matchCode}}})`; - code += `import createAdaptiveProxy from "arc-server/proxy";\n`; + if (hasNamedExports || hasDefaultExport) { + const proxyCode = `/*@__PURE__*/createAdaptiveProxy({default:${defaultId},match(f){return ${matchCode}}})`; + code += `import createAdaptiveProxy from "arc-server/proxy";\n`; - if (hasNamedExports) { - syntheticNamedExports = "_"; - code += `export const _ = ${proxyCode};\n`; + if (hasNamedExports) { + syntheticNamedExports = "_"; + code += `export const _ = ${proxyCode};\n`; - if (info.hasDefaultExport) { - code += "export default _.default;\n"; - } - } else { - code += `export default ${proxyCode}.default;\n`; - } - } - } else { - code += "export {};\n"; + if (hasDefaultExport) { + code += "export default _.default;\n"; } - - return { - code, - syntheticNamedExports, - moduleSideEffects: "no-treeshake", - }; + } else { + code += `export default ${proxyCode}.default;\n`; } + } else { + code += "export {};\n"; } - } else if (isArcVirtualMatch(id)) { - const [arcSourceId, arcFlagSet] = decodeArcVirtualMatch(id); - const { meta, moduleSideEffects, syntheticNamedExports } = - this.getModuleInfo(arcSourceId)!; - const code = meta.arcSourceCode as string; - const arcFS = getArcFS(arcFlagSet); + return { code, - moduleSideEffects, syntheticNamedExports, - meta: { - ...meta, - arcSourceId, - arcFlagSet, - arcFS, - }, + moduleSideEffects: "no-treeshake", }; } return null; }, - buildEnd() { + closeBundle() { clearCache(); - adaptiveMatchesForId.clear(); + proxyModuleId = 0; + metaForProxy = new Map(); }, generateBundle(outputOptions, bundle, isWrite) { if (!isWrite) { @@ -264,6 +237,10 @@ function partsToString(parts, base, injectAttrs) { store.write({ serverEntryFiles }); }, }; + + function nextProxyId() { + return arcProxyPrefix + (proxyModuleId++).toString(36) + arcJsSuffix; + } } function isArcId(id: string) { @@ -273,11 +250,3 @@ function isArcId(id: string) { function isArcProxyId(id: string) { return id.startsWith(arcProxyPrefix); } - -function encodeArcProxyId(id: string) { - return arcProxyPrefix + encodeFileName(id) + arcSuffix; -} - -function decodeArcProxyId(id: string) { - return decodeFileName(id.slice(arcProxyPrefix.length, -arcSuffix.length)); -} diff --git a/src/plugins/build-web.ts b/src/plugins/build-web.ts index 0cc48d6..304ece9 100644 --- a/src/plugins/build-web.ts +++ b/src/plugins/build-web.ts @@ -1,27 +1,25 @@ import { promises as fs } from "fs"; import path from "path"; import type * as estree from "estree"; -import type { Plugin } from "vite"; -import { getArcFS } from "../utils/arc-fs"; -import { ensureArcPluginIsFirst } from "../utils/ensure-arc-plugin-is-first"; +import type { Plugin, Rollup } from "vite"; +import { isAssetFile, isGlobalCSSFile } from "../utils/file-types"; import { decodeFileName, encodeFileName } from "../utils/filename-encoding"; import { type FlagSet, compareFlaggedObject, hasFlags } from "../utils/flags"; import { indexToId } from "../utils/index-to-id"; -import { isCssFile } from "../utils/is-css-file"; import { type DocManifest, generatManifest, generateHTML, } from "../utils/manifest"; -import { type Matches } from "../utils/matches"; +import { getMatches, type Matches } from "../utils/matches"; import { type InternalPluginOptions } from "../utils/options"; import { prepareArcEntryHTML } from "../utils/prepare-arc-entry-html"; import { stripEntryScript } from "../utils/strip-entry-script"; -import { - decodeArcVirtualMatch, - getVirtualMatches, - isArcVirtualMatch, -} from "../utils/virtual-matches"; + +interface ProxyMeta { + resolved: Rollup.ResolvedId; + matches: Matches; +} const arcPrefix = "\0arc-"; const arcJsSuffix = ".mjs"; @@ -36,8 +34,8 @@ export function pluginBuildWeb({ const apply: Plugin["apply"] = (config, { command }) => command === "build" && !config.build?.ssr; let globalIds = new Map(); + let metaForProxy = new Map(); let adaptiveImporters = new Map>(); - let adaptiveMatchesForId = new Map(); let bindingsByAdaptiveId = new Map | true>(); let metaForAdaptiveChunk = new Map< string, @@ -57,9 +55,6 @@ export function pluginBuildWeb({ name: "arc-vite:build-web", enforce: "pre", apply, - config(config) { - ensureArcPluginIsFirst(config.plugins!); - }, configResolved(config) { basePath = config.base; const { renderBuiltUrl: originalRenderBuiltURL } = config.experimental; @@ -85,9 +80,9 @@ export function pluginBuildWeb({ proxyModuleId = initModuleId = 0; globalIds = new Map(); adaptiveImporters = new Map(); - adaptiveMatchesForId = new Map(); bindingsByAdaptiveId = new Map(); metaForAdaptiveChunk = new Map(); + metaForProxy = new Map(); }, async resolveId(source, importer, options) { if (importer) { @@ -96,14 +91,6 @@ export function pluginBuildWeb({ } if (isArcId(importer)) { - if (isArcVirtualMatch(importer)) { - return this.resolve( - source, - this.getModuleInfo(importer)?.meta.arcSourceId, - options, - ); - } - return source; } @@ -113,16 +100,9 @@ export function pluginBuildWeb({ }); if (resolved && !resolved.external) { - const matches = await getVirtualMatches(this, flagSets, resolved); + const { id } = resolved; + const matches = getMatches(id, flagSets); if (matches) { - const { id } = resolved; - - if (!this.getModuleInfo(id)?.ast) { - await this.load(resolved); - } - - adaptiveMatchesForId.set(id, matches); - const adaptiveImportsForImporter = adaptiveImporters.get(importer); if (adaptiveImportsForImporter) { @@ -131,7 +111,9 @@ export function pluginBuildWeb({ adaptiveImporters.set(importer, new Map([[source, id]])); } - return { id: encodeArcProxyId(id) }; + const proxyId = nextProxyId(); + metaForProxy.set(proxyId, { resolved, matches }); + return proxyId; } } @@ -156,7 +138,7 @@ export function pluginBuildWeb({ seenImports.add(childId); if (isArcProxyId(childId)) { - adaptiveImports.push(decodeArcProxyId(childId)); + adaptiveImports.push(childId); } else { const info = await this.load({ id: childId, @@ -196,32 +178,25 @@ export function pluginBuildWeb({ pendingAdaptiveImports = []; await Promise.all( pending.map(async (adaptiveImport) => { - const adaptiveMatches = - adaptiveMatchesForId.get(adaptiveImport); - - if (adaptiveMatches) { - let adaptedImport: string; - for (const { - flags, - value, - } of adaptiveMatches.alternates) { - if (hasFlags(flagSet, flags)) { - adaptedImport = value; - break; - } + const { matches } = metaForProxy.get(adaptiveImport)!; + let adaptedImport: string; + for (const { flags, value } of matches.alternates) { + if (hasFlags(flagSet, flags)) { + adaptedImport = value; + break; } - - adaptedImport ||= adaptiveMatches.default; - resolvedAdaptiveImports.set( - adaptiveImport, - adaptedImport, - ); - await scanImports( - adaptedImport, - importsForFlagSet, - pendingAdaptiveImports, - ); } + + adaptedImport ||= matches.default; + resolvedAdaptiveImports.set( + adaptiveImport, + adaptedImport, + ); + await scanImports( + adaptedImport, + importsForFlagSet, + pendingAdaptiveImports, + ); }), ); } @@ -317,48 +292,57 @@ export function pluginBuildWeb({ moduleSideEffects: "no-treeshake", }; } else if (isArcProxyId(id)) { - id = decodeArcProxyId(id); + const { resolved } = metaForProxy.get(id)!; + let code = ""; - if (isCssFile(id)) { + if (isGlobalCSSFile(resolved.id)) { return { code: "" }; } - const info = this.getModuleInfo(id); - if (info) { - let code = ""; - let syntheticNamedExports: boolean | string = false; + let hasNamedExports = false; + let hasDefaultExport = false; + let syntheticNamedExports: boolean | string = false; - if (info.exports?.length) { - const hasNamedExports = - info.exports.length >= (info.hasDefaultExport ? 2 : 1); + if (isAssetFile(resolved.id)) { + hasDefaultExport = true; + } else { + let info = this.getModuleInfo(resolved.id); + if (!info?.ast) { + info = await this.load(resolved); + } - if (hasNamedExports || info.hasDefaultExport) { - const arcId = getArcId(id); - if (hasNamedExports) { - code += `export const {${arcId}} = ${runtimeId};\n`; - syntheticNamedExports = arcId; + if (info.exports) { + hasDefaultExport = info.hasDefaultExport === true; + hasNamedExports = + info.exports.length >= (hasDefaultExport ? 2 : 1); + } + } - if (info.hasDefaultExport) { - code += `export default ${arcId}.default;\n`; - } - } else { - code += `export default ${runtimeId}.${arcId}.default;\n`; - } + if (hasNamedExports || hasDefaultExport) { + const arcId = getArcId(id); + if (hasNamedExports) { + code += `export const {${arcId}} = ${runtimeId};\n`; + syntheticNamedExports = arcId; + + if (hasDefaultExport) { + code += `export default ${arcId}.default;\n`; } } else { - code = "export {};\n"; + code += `export default ${runtimeId}.${arcId}.default;\n`; } - - return { - code, - syntheticNamedExports, - moduleSideEffects: false, - }; + } else { + code = "export {};\n"; } + + return { + code, + syntheticNamedExports, + moduleSideEffects: false, + }; } else if (isArcInitId(id)) { const [adaptiveImport, adaptedImport] = decodeArcInitId(id); const bindings = bindingsByAdaptiveId.get(adaptiveImport); - if (!bindings || isCssFile(adaptiveImport)) { + if (!bindings || isGlobalCSSFile(adaptiveImport)) { return { code: `import ${JSON.stringify(adaptedImport)};\n`, moduleSideEffects: "no-treeshake", @@ -382,23 +366,6 @@ export function pluginBuildWeb({ };\n`, moduleSideEffects: "no-treeshake", }; - } else if (isArcVirtualMatch(id)) { - const [arcSourceId, arcFlagSet] = decodeArcVirtualMatch(id); - const { meta, moduleSideEffects, syntheticNamedExports } = - this.getModuleInfo(arcSourceId)!; - const code = meta.arcSourceCode as string; - const arcFS = getArcFS(arcFlagSet); - return { - code, - moduleSideEffects, - syntheticNamedExports, - meta: { - ...meta, - arcSourceId, - arcFlagSet, - arcFS, - }, - }; } return null; @@ -515,10 +482,8 @@ export function pluginBuildWeb({ }, ]; - function encodeArcProxyId(id: string) { - return `${arcProxyPrefix + (proxyModuleId++).toString(36)}:${ - encodeFileName(id) + arcJsSuffix - }`; + function nextProxyId() { + return arcProxyPrefix + (proxyModuleId++).toString(36) + arcJsSuffix; } function encodeArcInitId(adaptiveImport: string, adaptedImport: string) { @@ -556,15 +521,6 @@ function isArcInitId(id: string) { return id.startsWith(arcInitPrefix); } -function decodeArcProxyId(id: string) { - return decodeFileName( - id.slice( - id.indexOf(":", arcProxyPrefix.length + 1) + 1, - -arcJsSuffix.length, - ), - ); -} - function decodeArcInitId(id: string) { const prefixEnd = id.indexOf(":", arcInitPrefix.length + 1) + 1; const sepStart = id.indexOf(",", prefixEnd); diff --git a/src/plugins/serve.ts b/src/plugins/serve.ts index 10605fa..df8a12d 100644 --- a/src/plugins/serve.ts +++ b/src/plugins/serve.ts @@ -1,9 +1,6 @@ -import fs from "fs"; -import { createRequire, syncBuiltinESMExports } from "module"; +import { createRequire } from "module"; import path from "path"; import type { Plugin } from "vite"; -import { patchFS } from "../utils/arc-fs"; -import { ensureArcPluginIsFirst } from "../utils/ensure-arc-plugin-is-first"; import { type FlagSet, hasFlags } from "../utils/flags"; import { getMatches } from "../utils/matches"; import { type InternalPluginOptions } from "../utils/options"; @@ -15,7 +12,6 @@ export function pluginServe({ forceFlagSet, }: InternalPluginOptions): Plugin { const flagSet = forceFlagSet?.length ? forceFlagSet : undefined; - let restoreFS: ReturnType | undefined; return { name: "arc-vite:serve", enforce: "pre", @@ -32,9 +28,6 @@ export function pluginServe({ if (!flagSet) return; - syncBuiltinESMExports(); - restoreFS = patchFS(flagSet, fs); - ensureArcPluginIsFirst(config.plugins!); config.cacheDir = path.resolve( `node_modules/.vite/arc/${flagSet.join(".")}`, ); @@ -54,11 +47,7 @@ export function pluginServe({ }, ); build.onLoad({ filter: /./ }, (args) => { - const adaptedImport = getAdaptedMatch( - args.path, - flagSets, - flagSet!, - ); + const adaptedImport = getAdaptedMatch(args.path, flagSets, flagSet); if (adaptedImport) { const proxiedImportCode = JSON.stringify( arcProxyPrefix + adaptedImport, @@ -79,24 +68,12 @@ export function pluginServe({ skipSelf: true, }); if (resolved) { - const adaptedImport = getAdaptedMatch(resolved.id, flagSets, flagSet); - if (adaptedImport) { - return { id: adaptedImport }; - } + return getAdaptedMatch(resolved.id, flagSets, flagSet) || resolved; } - - return resolved; } return null; }, - buildEnd() { - if (restoreFS) { - restoreFS(); - syncBuiltinESMExports(); - restoreFS = undefined; - } - }, }; } diff --git a/src/tests/fixtures/marko-scan-files/config.ts b/src/tests/fixtures/marko-scan-files/config.ts deleted file mode 100644 index 55964e7..0000000 --- a/src/tests/fixtures/marko-scan-files/config.ts +++ /dev/null @@ -1,18 +0,0 @@ -import markoVite from "@marko/vite"; -import { defineConfig } from "vite"; -import arcVite from "../../.."; -export default () => - defineConfig({ - plugins: [ - markoVite(), - arcVite({ - flags: ["mobile"], - }), - ], - build: { - modulePreload: false, - minify: false, - target: "esnext", - emptyOutDir: false, - }, - }); diff --git a/src/tests/fixtures/marko-scan-files/src/components/app.marko b/src/tests/fixtures/marko-scan-files/src/components/app.marko deleted file mode 100644 index bc6abcf..0000000 --- a/src/tests/fixtures/marko-scan-files/src/components/app.marko +++ /dev/null @@ -1,5 +0,0 @@ - -

- got: ${value} -

- diff --git a/src/tests/fixtures/marko-scan-files/src/components/maybe-stateful.marko b/src/tests/fixtures/marko-scan-files/src/components/maybe-stateful.marko deleted file mode 100644 index 20fe75e..0000000 --- a/src/tests/fixtures/marko-scan-files/src/components/maybe-stateful.marko +++ /dev/null @@ -1,5 +0,0 @@ -export interface Input { - renderBody: Marko.Body<[number]> -} - -<${input.renderBody}(1)/> diff --git a/src/tests/fixtures/marko-scan-files/src/components/maybe-stateful[mobile].marko b/src/tests/fixtures/marko-scan-files/src/components/maybe-stateful[mobile].marko deleted file mode 100644 index bfc8d30..0000000 --- a/src/tests/fixtures/marko-scan-files/src/components/maybe-stateful[mobile].marko +++ /dev/null @@ -1,21 +0,0 @@ -export interface Input { - renderBody: Marko.Body<[number]> -} - -class { - declare state: { mounted: boolean; count: number }; - onCreate() { - this.state = { mounted: false, count: 0 }; - } - onMount() { - this.state.mounted = true; - } - handleClick() { - this.state.count++; - } -} - -<${input.renderBody}(state.count)/> - diff --git a/src/tests/fixtures/marko-scan-files/src/entry-server.ts b/src/tests/fixtures/marko-scan-files/src/entry-server.ts deleted file mode 100644 index b838e02..0000000 --- a/src/tests/fixtures/marko-scan-files/src/entry-server.ts +++ /dev/null @@ -1,11 +0,0 @@ -import type { IncomingMessage } from "http"; -import { setFlags } from "arc-server"; -import template from "./index.marko"; - -export function render(req: IncomingMessage) { - const url = new URL(req.url!, `http://${req.headers.host}`); - if (url.pathname !== "/") return; - - setFlags({ mobile: url.searchParams.has("mobile") }); - return template.render({}); -} diff --git a/src/tests/fixtures/marko-scan-files/src/index.marko b/src/tests/fixtures/marko-scan-files/src/index.marko deleted file mode 100644 index 35a0b0b..0000000 --- a/src/tests/fixtures/marko-scan-files/src/index.marko +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - Document - - - - - diff --git a/src/tests/fixtures/marko/config.ts b/src/tests/fixtures/marko/config.ts index 55964e7..df64327 100644 --- a/src/tests/fixtures/marko/config.ts +++ b/src/tests/fixtures/marko/config.ts @@ -4,10 +4,10 @@ import arcVite from "../../.."; export default () => defineConfig({ plugins: [ - markoVite(), arcVite({ flags: ["mobile"], }), + markoVite(), ], build: { modulePreload: false, diff --git a/src/tests/marko-scan-files.test.ts b/src/tests/marko-scan-files.test.ts deleted file mode 100644 index ebaff42..0000000 --- a/src/tests/marko-scan-files.test.ts +++ /dev/null @@ -1,88 +0,0 @@ -import path from "node:path"; -import * as t from "node:test"; -import url from "node:url"; -import { expect } from "@playwright/test"; -import { type Page } from "playwright"; -import { createDevServer } from "./utils/dev-server"; -import { getPage } from "./utils/get-page"; -import { createProdServer } from "./utils/prod-server"; - -const fixture = path.join( - url.fileURLToPath(import.meta.url), - "../fixtures/marko-scan-files", -); - -t.test("marko-scan-files", async (t) => { - t.beforeEach(() => { - delete process.env.FLAGS; - }); - - await t.test("dev", async (t) => { - await t.test("FLAGS=", async (t) => { - process.env.FLAGS = ""; - const [{ page, port }, server] = await Promise.all([ - getPage(t), - createDevServer(fixture), - ]); - - t.after(await server.listen(port)); - - await page.goto("/"); - - await t.test("has desktop content", async () => { - await assertHasDesktopContent(page); - }); - }); - - await t.test("FLAGS=mobile", async (t) => { - process.env.FLAGS = "mobile"; - const [{ page, port }, server] = await Promise.all([ - getPage(t), - createDevServer(fixture), - ]); - - t.after(await server.listen(port)); - - await page.goto("/"); - - await t.test("has mobile content", async () => { - await assertHasMobileContent(page); - }); - }); - }); - - await t.test("prod", async (t) => { - const [{ page, port }, server] = await Promise.all([ - getPage(t), - createProdServer(fixture), - ]); - - t.after(await server.listen(port)); - - await t.test("FLAGS=", async (t) => { - await page.goto("/"); - await t.test("has desktop content", async () => { - await assertHasDesktopContent(page); - }); - }); - - await t.test("FLAGS=mobile", async (t) => { - await page.goto("/?mobile"); - await t.test("has mobile content", async () => { - await assertHasMobileContent(page); - }); - }); - }); -}); - -async function assertHasDesktopContent(page: Page) { - await expect(page.getByRole("heading")).toContainText(/got: 1/); -} - -async function assertHasMobileContent(page: Page) { - await expect(page.getByRole("heading")).toContainText(/got: 0/); - await page - .getByRole("button", { name: "Increment", disabled: false }) - .click(); - await expect(page.getByRole("heading")).toContainText(/got: 1/); -} diff --git a/src/utils/arc-fs.ts b/src/utils/arc-fs.ts deleted file mode 100644 index e86fb18..0000000 --- a/src/utils/arc-fs.ts +++ /dev/null @@ -1,131 +0,0 @@ -import fs from "fs"; -import type { FlagSet } from "./flags"; -import { getMatches } from "./matches"; - -const arcFlagReg = /\[/; -const fsByFlagSet = new Map(); - -export function getArcFS(flagSet: FlagSet) { - const flags = flagSet.join("."); - let fileSystem = fsByFlagSet.get(flags); - if (!fileSystem) { - fileSystem = createAdaptiveFS(flagSet); - fsByFlagSet.set(flags, fileSystem); - } - - return fileSystem; -} - -export function patchFS(flagSet: FlagSet, afs: any) { - const flagSets = [flagSet]; - - // Sync api - const { readFileSync, statSync, readlinkSync, accessSync, readdirSync } = fs; - afs.readFileSync = (id: string, ...args: any[]) => - readFileSync(getMatch(id), ...args); - afs.statSync = (id: string, ...args: any[]) => - statSync(getMatch(id), ...args); - afs.readlinkSync = (id: string, ...args: any[]) => - readlinkSync(getMatch(id), ...args); - afs.accessSync = (id: string, ...args: any[]) => - accessSync(getMatch(id), ...args); - afs.readdirSync = (id: string, ...args: any[]) => { - const match = getMatch(id); - const entries = readdirSync(id, ...args); - return match === id ? entries : ignoreAdaptiveEntries(entries); - }; - - // Callback api - const { readFile, stat, readlink, access, readdir } = fs as any; - afs.readFile = (id: string, ...args: any[]) => - readFile(getMatch(id), ...args); - afs.stat = (id: string, ...args: any[]) => stat(getMatch(id), ...args); - afs.readlink = (id: string, ...args: any[]) => - readlink(getMatch(id), ...args); - afs.access = (id: string, ...args: any[]) => access(getMatch(id), ...args); - afs.readdir = (id: string, ...args: any[]) => { - const match = getMatch(id); - if (match === id) return readdir(id, ...args); - const cb = args.pop(); - readdir(id, ...args, (err: any, entries: string[]) => { - if (err) return cb(err); - cb(null, ignoreAdaptiveEntries(entries)); - }); - }; - - // Promise api - const { - promises: { - readFile: readFilePromise, - stat: statPromise, - readlink: readlinkPromise, - access: accessPromise, - readdir: readdirPromise, - }, - } = fs; - afs.promises.readFile = (id: string, ...args: any[]) => - readFilePromise(getMatch(id), ...args); - afs.promises.stat = (id: string, ...args: any[]) => - statPromise(getMatch(id), ...args); - afs.promises.readlink = (id: string, ...args: any[]) => - readlinkPromise(getMatch(id), ...args); - afs.promises.access = (id: string, ...args: any[]) => - accessPromise(getMatch(id), ...args); - afs.promises.readdir = (id: string, ...args: any[]) => { - const match = getMatch(id); - const entriesPromise = readdirPromise(id, ...args); - return match === id - ? entriesPromise - : entriesPromise.then(ignoreAdaptiveEntries); - }; - - return () => { - afs.readFileSync = readFileSync; - afs.statSync = statSync; - afs.readlinkSync = readlinkSync; - afs.accessSync = accessSync; - afs.readdirSync = readdirSync; - - afs.readFile = readFile; - afs.stat = stat; - afs.readlink = readlink; - afs.access = access; - afs.readdir = readdir; - - afs.promises.readFile = readFilePromise; - afs.promises.stat = statPromise; - afs.promises.readlink = readlinkPromise; - afs.promises.access = accessPromise; - afs.promises.readdir = readdirPromise; - }; - - function getMatch(id: unknown): string { - if (typeof id !== "string") return id as string; - const match = getMatches(id, flagSets); - return match ? match.alternates[0].value : id; - } -} - -function createAdaptiveFS(flagSet: FlagSet) { - const afs = { ...fs, promises: { ...fs.promises } } as any; - patchFS(flagSet, afs); - return afs as typeof fs; -} - -function ignoreAdaptiveEntries(entries: string[]) { - for (let i = 0; i < entries.length; i++) { - const entry = entries[i]; - if (arcFlagReg.test(entry)) { - const uniqueEntries = entries.slice(0, i); - for (; i < entries.length; i++) { - const entry = entries[i]; - if (!arcFlagReg.test(entry)) { - uniqueEntries.push(entry); - } - } - - return uniqueEntries; - } - } - return entries; -} diff --git a/src/utils/ensure-arc-plugin-is-first.ts b/src/utils/ensure-arc-plugin-is-first.ts deleted file mode 100644 index b541b1b..0000000 --- a/src/utils/ensure-arc-plugin-is-first.ts +++ /dev/null @@ -1,18 +0,0 @@ -import type * as vite from "vite"; -const arcVitePluginNameReg = /^arc-vite/; -export function ensureArcPluginIsFirst(plugins: vite.PluginOption[]) { - for (let i = plugins.length; i--; ) { - const plugin = plugins[i]; - if (!Array.isArray(plugin)) continue; - const [firstPlugin] = plugin; - if ( - firstPlugin && - typeof firstPlugin === "object" && - "name" in firstPlugin && - arcVitePluginNameReg.test(firstPlugin.name) - ) { - plugins[i] = false; - plugins.unshift(plugin); - } - } -} diff --git a/src/utils/file-types.ts b/src/utils/file-types.ts new file mode 100644 index 0000000..ee24bb2 --- /dev/null +++ b/src/utils/file-types.ts @@ -0,0 +1,12 @@ +const assetFileReg = + /\.(?:a?png|jpe?g|jfif|pipeg|pjp|gif|svg|ico|web[pm]|avif|mp4|ogg|mp3|wav|flac|aac|opus|woff2?|eot|[ot]tf|webmanifest|pdf|txt)(\?|$)/; +const globalCSSFileReg = + /(? { - const matches = await getVirtualMatches(ctx, flagSets, { id }); - if (matches) { - for (const alternate of matches.alternates) { - matchesFlagSets.push(alternate.flags); - } - } - }), - ); - - let alternates: undefined | [Match, ...Match[]]; - for (const flagSet of normalizeFlagSets(matchesFlagSets)) { - if (!flagSet.length) continue; - - const alternate: Match = { - flags: flagSet, - value: encodeArcVirtualMatch(id, flagSet), - }; - - if (alternates) { - alternates.push(alternate); - } else { - alternates = [alternate]; - } - } - - if (alternates) { - return { - default: id, - alternates, - }; - } - } -} - -export function isArcVirtualMatch(id: string) { - return id.startsWith(arcVirtualMatchPrefix); -} - -export function decodeArcVirtualMatch( - id: string, -): readonly [adaptiveImport: string, flagSet: FlagSet] { - const prefixEnd = id.indexOf(":", arcVirtualMatchPrefix.length + 1) + 1; - const adaptiveImport = decodeFileName( - id.slice(arcVirtualMatchPrefix.length, prefixEnd - 1), - ); - const flagSet = decodeFileName(id.slice(prefixEnd)).split(".") as FlagSet; - return [adaptiveImport, flagSet]; -} - -function encodeArcVirtualMatch(id: string, flagSet: FlagSet) { - return ( - arcVirtualMatchPrefix + - encodeFileName(id) + - ":" + - encodeFileName(flagSet.join(".")) - ); -}