diff --git a/.changeset/thirty-phones-pretend.md b/.changeset/thirty-phones-pretend.md new file mode 100644 index 0000000..0ced225 --- /dev/null +++ b/.changeset/thirty-phones-pretend.md @@ -0,0 +1,5 @@ +--- +"arc-vite": minor +--- + +Add support for other Vite plugins registering additional files to check when determining if a module is adaptive. diff --git a/.eslintrc.json b/.eslintrc.json index 3d57b03..1f74204 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -10,23 +10,31 @@ }, "settings": { "import/resolver": { - "typescript": { - "project": true - } + "typescript": true } }, "env": { "node": true, - "browser": true, - "es2024": true + "es2024": true, + "browser": true }, "rules": { - "import/order": ["error"], - "sort-imports": [ + "import/order": [ "error", { - "allowSeparatedGroups": true, - "ignoreDeclarationSort": true + "groups": [ + ["builtin"], + ["external"], + "internal", + "parent", + "sibling", + "index" + ], + "alphabetize": { + "order": "asc", + "orderImportKind": "asc", + "caseInsensitive": true + } } ] }, @@ -36,9 +44,9 @@ "parser": "@typescript-eslint/parser", "extends": [ "eslint:recommended", + "plugin:@typescript-eslint/recommended", "plugin:import/recommended", "plugin:import/typescript", - "plugin:@typescript-eslint/recommended", "prettier" ], "rules": { diff --git a/package-lock.json b/package-lock.json index 0e7893c..0f338d6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -17,27 +17,27 @@ "devDependencies": { "@changesets/changelog-github": "^0.4.8", "@changesets/cli": "^2.26.2", - "@marko/compiler": "^5.33.7", - "@marko/vite": "^4.0.0", + "@marko/compiler": "^5.33.8", + "@marko/vite": "^4.0.2", "@playwright/test": "^1.39.0", "@types/estree": "^1.0.5", "@types/node": "^20.9.0", "@types/serve-handler": "^6.1.4", - "@typescript-eslint/eslint-plugin": "^6.10.0", - "@typescript-eslint/parser": "^6.10.0", + "@typescript-eslint/eslint-plugin": "^6.11.0", + "@typescript-eslint/parser": "^6.11.0", "c8": "^8.0.1", "eslint": "^8.53.0", "eslint-config-prettier": "^9.0.0", "eslint-import-resolver-typescript": "^3.6.1", "eslint-plugin-import": "^2.29.0", "husky": "^8.0.3", - "lint-staged": "^15.0.2", - "marko": "^5.31.17", + "lint-staged": "^15.1.0", + "marko": "^5.31.18", "playwright": "^1.39.0", "prettier": "^3.1.0", "serve-handler": "^6.1.5", "sort-package-json": "^2.6.0", - "tsx": "^4.1.0", + "tsx": "^4.1.2", "typescript": "^5.2.2", "vite": "^4.5.0" }, @@ -839,6 +839,54 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/@esbuild/android-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.18.20.tgz", + "integrity": "sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz", + "integrity": "sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.18.20.tgz", + "integrity": "sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@esbuild/darwin-arm64": { "version": "0.18.20", "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.18.20.tgz", @@ -855,6 +903,294 @@ "node": ">=12" } }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.18.20.tgz", + "integrity": "sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.18.20.tgz", + "integrity": "sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.18.20.tgz", + "integrity": "sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.18.20.tgz", + "integrity": "sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.18.20.tgz", + "integrity": "sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.18.20.tgz", + "integrity": "sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.18.20.tgz", + "integrity": "sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==", + "cpu": [ + "loong64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.18.20.tgz", + "integrity": "sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==", + "cpu": [ + "mips64el" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.18.20.tgz", + "integrity": "sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.18.20.tgz", + "integrity": "sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.18.20.tgz", + "integrity": "sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.18.20.tgz", + "integrity": "sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.18.20.tgz", + "integrity": "sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.18.20.tgz", + "integrity": "sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.18.20.tgz", + "integrity": "sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.18.20.tgz", + "integrity": "sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.18.20.tgz", + "integrity": "sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==", + "cpu": [ + "ia32" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.18.20", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.18.20.tgz", + "integrity": "sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -1113,9 +1449,9 @@ } }, "node_modules/@marko/babel-utils": { - "version": "6.3.3", - "resolved": "https://registry.npmjs.org/@marko/babel-utils/-/babel-utils-6.3.3.tgz", - "integrity": "sha512-dVrND1+KLzvm53DtRi9uag53qRFNq6MgKH9LOnEbJIz0Sz2Dg49R3dPOX8Z+9kgyJHGblofnE5zlttUa9RmXew==", + "version": "6.3.4", + "resolved": "https://registry.npmjs.org/@marko/babel-utils/-/babel-utils-6.3.4.tgz", + "integrity": "sha512-qS0YEi3K25jUScTTHSkj2IqYTLpUJXT9RAkh8wmw2bxMP7T5wKDJlVpcbGlBt+bZSFTxAkM3eoRo8SMj4rmi3Q==", "dev": true, "dependencies": { "@babel/runtime": "^7.16.0", @@ -1124,9 +1460,9 @@ } }, "node_modules/@marko/compiler": { - "version": "5.33.7", - "resolved": "https://registry.npmjs.org/@marko/compiler/-/compiler-5.33.7.tgz", - "integrity": "sha512-92ZEPyuvFS6eIatXbu61J/Y7n+4MilHxqvytH71kaOgfabjJinx4uQV1qT4FRg3YGTbgUjtCqxyaqhvOIeECzA==", + "version": "5.33.8", + "resolved": "https://registry.npmjs.org/@marko/compiler/-/compiler-5.33.8.tgz", + "integrity": "sha512-hPdPZDlJ83zlfb4J2iU++Jbrw0nS7EDcajn9jeo5iDjHeKc4UjIPrLsDNVqvIEbtEB2SMjCz6a7ZGU23WQWc2g==", "dev": true, "dependencies": { "@babel/code-frame": "^7.16.0", @@ -1139,7 +1475,7 @@ "@babel/runtime": "^7.16.0", "@babel/traverse": "^7.16.0", "@babel/types": "^7.16.0", - "@marko/babel-utils": "^6.3.3", + "@marko/babel-utils": "^6.3.4", "complain": "^1.6.0", "he": "^1.2.0", "htmljs-parser": "^5.4.3", @@ -1156,13 +1492,13 @@ } }, "node_modules/@marko/translator-default": { - "version": "5.31.7", - "resolved": "https://registry.npmjs.org/@marko/translator-default/-/translator-default-5.31.7.tgz", - "integrity": "sha512-ZFIWcE2WBEm4re8o7AJ4bC75Znz2cGBJH99I98F2Aj/gmM0+FdxjXLYM8ykXCssAQsoJ5yTo8X/Po5tugYN0fg==", + "version": "5.31.8", + "resolved": "https://registry.npmjs.org/@marko/translator-default/-/translator-default-5.31.8.tgz", + "integrity": "sha512-TIzmH1EbFA4I0BpaikukwMDMFA2v0GLcWuikQxDlAqEAeITKAZ6W/ygilGkPgcYZlEu7LUtKPKKNnFyXH0w30Q==", "dev": true, "dependencies": { "@babel/runtime": "^7.16.0", - "@marko/babel-utils": "^6.3.3", + "@marko/babel-utils": "^6.3.4", "escape-string-regexp": "^4.0.0", "magic-string": "^0.27.0", "self-closing-tags": "^1.0.1" @@ -1185,9 +1521,9 @@ } }, "node_modules/@marko/vite": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@marko/vite/-/vite-4.0.0.tgz", - "integrity": "sha512-yeYTimM9xQxX1ct6EGTJmJG1rcQk/+DmcB1ErBW1qJB1h8o8qHk3sAGFw/faPrgODneD/V9wtwc1Viih14T61A==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@marko/vite/-/vite-4.0.2.tgz", + "integrity": "sha512-MDPVCldlxCn3odHe/o20LL62Ji3p4YCm2KA5aGFxujWfNzqMl+ccthPi5KK78rSkk/dtx041kawy71tpLsQdQg==", "dev": true, "dependencies": { "anymatch": "^3.1.3", @@ -1322,16 +1658,16 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.10.0.tgz", - "integrity": "sha512-uoLj4g2OTL8rfUQVx2AFO1hp/zja1wABJq77P6IclQs6I/m9GLrm7jCdgzZkvWdDCQf1uEvoa8s8CupsgWQgVg==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.11.0.tgz", + "integrity": "sha512-uXnpZDc4VRjY4iuypDBKzW1rz9T5YBBK0snMn8MaTSNd2kMlj50LnLBABELjJiOL5YHk7ZD8hbSpI9ubzqYI0w==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.10.0", - "@typescript-eslint/type-utils": "6.10.0", - "@typescript-eslint/utils": "6.10.0", - "@typescript-eslint/visitor-keys": "6.10.0", + "@typescript-eslint/scope-manager": "6.11.0", + "@typescript-eslint/type-utils": "6.11.0", + "@typescript-eslint/utils": "6.11.0", + "@typescript-eslint/visitor-keys": "6.11.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -1357,15 +1693,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.10.0.tgz", - "integrity": "sha512-+sZwIj+s+io9ozSxIWbNB5873OSdfeBEH/FR0re14WLI6BaKuSOnnwCJ2foUiu8uXf4dRp1UqHP0vrZ1zXGrog==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.11.0.tgz", + "integrity": "sha512-+whEdjk+d5do5nxfxx73oanLL9ghKO3EwM9kBCkUtWMRwWuPaFv9ScuqlYfQ6pAD6ZiJhky7TZ2ZYhrMsfMxVQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.10.0", - "@typescript-eslint/types": "6.10.0", - "@typescript-eslint/typescript-estree": "6.10.0", - "@typescript-eslint/visitor-keys": "6.10.0", + "@typescript-eslint/scope-manager": "6.11.0", + "@typescript-eslint/types": "6.11.0", + "@typescript-eslint/typescript-estree": "6.11.0", + "@typescript-eslint/visitor-keys": "6.11.0", "debug": "^4.3.4" }, "engines": { @@ -1385,13 +1721,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.10.0.tgz", - "integrity": "sha512-TN/plV7dzqqC2iPNf1KrxozDgZs53Gfgg5ZHyw8erd6jd5Ta/JIEcdCheXFt9b1NYb93a1wmIIVW/2gLkombDg==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.11.0.tgz", + "integrity": "sha512-0A8KoVvIURG4uhxAdjSaxy8RdRE//HztaZdG8KiHLP8WOXSk0vlF7Pvogv+vlJA5Rnjj/wDcFENvDaHb+gKd1A==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.10.0", - "@typescript-eslint/visitor-keys": "6.10.0" + "@typescript-eslint/types": "6.11.0", + "@typescript-eslint/visitor-keys": "6.11.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1402,13 +1738,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.10.0.tgz", - "integrity": "sha512-wYpPs3hgTFblMYwbYWPT3eZtaDOjbLyIYuqpwuLBBqhLiuvJ+9sEp2gNRJEtR5N/c9G1uTtQQL5AhV0fEPJYcg==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.11.0.tgz", + "integrity": "sha512-nA4IOXwZtqBjIoYrJcYxLRO+F9ri+leVGoJcMW1uqr4r1Hq7vW5cyWrA43lFbpRvQ9XgNrnfLpIkO3i1emDBIA==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.10.0", - "@typescript-eslint/utils": "6.10.0", + "@typescript-eslint/typescript-estree": "6.11.0", + "@typescript-eslint/utils": "6.11.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -1429,9 +1765,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.10.0.tgz", - "integrity": "sha512-36Fq1PWh9dusgo3vH7qmQAj5/AZqARky1Wi6WpINxB6SkQdY5vQoT2/7rW7uBIsPDcvvGCLi4r10p0OJ7ITAeg==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.11.0.tgz", + "integrity": "sha512-ZbEzuD4DwEJxwPqhv3QULlRj8KYTAnNsXxmfuUXFCxZmO6CF2gM/y+ugBSAQhrqaJL3M+oe4owdWunaHM6beqA==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1442,13 +1778,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.10.0.tgz", - "integrity": "sha512-ek0Eyuy6P15LJVeghbWhSrBCj/vJpPXXR+EpaRZqou7achUWL8IdYnMSC5WHAeTWswYQuP2hAZgij/bC9fanBg==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.11.0.tgz", + "integrity": "sha512-Aezzv1o2tWJwvZhedzvD5Yv7+Lpu1by/U1LZ5gLc4tCx8jUmuSCMioPFRjliN/6SJIvY6HpTtJIWubKuYYYesQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.10.0", - "@typescript-eslint/visitor-keys": "6.10.0", + "@typescript-eslint/types": "6.11.0", + "@typescript-eslint/visitor-keys": "6.11.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1469,17 +1805,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.10.0.tgz", - "integrity": "sha512-v+pJ1/RcVyRc0o4wAGux9x42RHmAjIGzPRo538Z8M1tVx6HOnoQBCX/NoadHQlZeC+QO2yr4nNSFWOoraZCAyg==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.11.0.tgz", + "integrity": "sha512-p23ibf68fxoZy605dc0dQAEoUsoiNoP3MD9WQGiHLDuTSOuqoTsa4oAy+h3KDkTcxbbfOtUjb9h3Ta0gT4ug2g==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.10.0", - "@typescript-eslint/types": "6.10.0", - "@typescript-eslint/typescript-estree": "6.10.0", + "@typescript-eslint/scope-manager": "6.11.0", + "@typescript-eslint/types": "6.11.0", + "@typescript-eslint/typescript-estree": "6.11.0", "semver": "^7.5.4" }, "engines": { @@ -1494,12 +1830,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.10.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.10.0.tgz", - "integrity": "sha512-xMGluxQIEtOM7bqFCo+rCMh5fqI+ZxV5RUUOa29iVPz1OgCZrtc7rFnz5cLUazlkPKYqX+75iuDq7m0HQ48nCg==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.11.0.tgz", + "integrity": "sha512-+SUN/W7WjBr05uRxPggJPSzyB8zUpaYo2hByKasWbqr3PM8AXfZt8UHdNpBS1v9SA62qnSSMF3380SwDqqprgQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.10.0", + "@typescript-eslint/types": "6.11.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -2068,9 +2404,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001561", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001561.tgz", - "integrity": "sha512-NTt0DNoKe958Q0BE0j0c1V9jbUzhBxHIEJy7asmGrpE0yG63KTV7PLHPnK2E1O9RsQrQ081I3NLuXGS6zht3cw==", + "version": "1.0.30001562", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001562.tgz", + "integrity": "sha512-kfte3Hym//51EdX4239i+Rmp20EsLIYGdPkERegTgU19hQWCRhsRFGKHTliUlsry53tv17K7n077Kqa0WJU4ng==", "dev": true, "funding": [ { @@ -2621,9 +2957,9 @@ "dev": true }, "node_modules/electron-to-chromium": { - "version": "1.4.580", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.580.tgz", - "integrity": "sha512-T5q3pjQon853xxxHUq3ZP68ZpvJHuSMY2+BZaW3QzjS4HvNuvsMmZ/+lU+nCrftre1jFZ+OSlExynXWBihnXzw==", + "version": "1.4.584", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.584.tgz", + "integrity": "sha512-rXCtDiXCBtfTfEegkthruCvyWZnr1/FCrUGY/nYQiF+lSZDmwQBDxp0rivZxV8trXb6cbgojhcSTW5xsDcHQ8g==", "dev": true }, "node_modules/emoji-regex": { @@ -3504,9 +3840,9 @@ } }, "node_modules/flat-cache": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.1.tgz", - "integrity": "sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, "dependencies": { "flatted": "^3.2.9", @@ -3514,7 +3850,7 @@ "rimraf": "^3.0.2" }, "engines": { - "node": ">=12.0.0" + "node": "^10.12.0 || >=12.0.0" } }, "node_modules/flatted": { @@ -3927,9 +4263,9 @@ "dev": true }, "node_modules/htmljs-parser": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/htmljs-parser/-/htmljs-parser-5.5.0.tgz", - "integrity": "sha512-KcJ84GmLFo+EWQ1hYjgLTSro8kEMFuTTdchOUeco5N95peHMtoY5XsX7YeU4oj+tPrvl9OXHlUkJAnY5zshhaQ==", + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/htmljs-parser/-/htmljs-parser-5.5.1.tgz", + "integrity": "sha512-mzTQUcQFDLS6LN20AiPKxG5dXmQOhej67ztIpnDzz5raAxrNsh8A17Wqef7KLWI4yJ4Uolj3syJmddFPdATn2Q==", "dev": true }, "node_modules/htmlparser2": { @@ -4597,9 +4933,9 @@ "dev": true }, "node_modules/lint-staged": { - "version": "15.0.2", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.0.2.tgz", - "integrity": "sha512-vnEy7pFTHyVuDmCAIFKR5QDO8XLVlPFQQyujQ/STOxe40ICWqJ6knS2wSJ/ffX/Lw0rz83luRDh+ET7toN+rOw==", + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.1.0.tgz", + "integrity": "sha512-ZPKXWHVlL7uwVpy8OZ7YQjYDAuO5X4kMh0XgZvPNxLcCCngd0PO5jKQyy3+s4TL2EnHoIXIzP1422f/l3nZKMw==", "dev": true, "dependencies": { "chalk": "5.3.0", @@ -4611,7 +4947,7 @@ "micromatch": "4.0.5", "pidtree": "0.6.0", "string-argv": "0.3.2", - "yaml": "2.3.3" + "yaml": "2.3.4" }, "bin": { "lint-staged": "bin/lint-staged.js" @@ -4792,13 +5128,13 @@ } }, "node_modules/marko": { - "version": "5.31.17", - "resolved": "https://registry.npmjs.org/marko/-/marko-5.31.17.tgz", - "integrity": "sha512-QC75Mlsq05LAvL2F/EPm0eLJREzVCo/nQMfcf48uwsqlGr+eAUjlU/m+yN3yLctb95iLjrny7qx5iVKvrCaIBg==", + "version": "5.31.18", + "resolved": "https://registry.npmjs.org/marko/-/marko-5.31.18.tgz", + "integrity": "sha512-kzPHPgib+EuktFtaN5SMxt22FyNTR2wRgUVaFrQO+7RYJepM3xDbi0NUp+vCn2kK0LqMFgLiVr2h/j/l+hw/cw==", "dev": true, "dependencies": { - "@marko/compiler": "^5.33.7", - "@marko/translator-default": "^5.31.7", + "@marko/compiler": "^5.33.8", + "@marko/translator-default": "^5.31.8", "app-module-path": "^2.2.0", "argly": "^1.2.0", "browser-refresh-client": "1.1.4", @@ -6814,9 +7150,9 @@ } }, "node_modules/tsx": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.1.0.tgz", - "integrity": "sha512-u4l17Yd63Wsk2fzNn1wZCmcS9kwJ/2ysl7wuoVggv2hd3NjLA5JQPpyJMXoWSXOwOvoQUzNcu/sf/35HEsnXsg==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.1.2.tgz", + "integrity": "sha512-1spM1bFV6MP2s4tO4tDC7g52fsaFdtEWdO4GfGdqi20qUgPbnAJqixOyIAvCSx1DDj3YIUB4CD06owTWUsOAuQ==", "dev": true, "dependencies": { "esbuild": "~0.18.20", @@ -7384,9 +7720,9 @@ "dev": true }, "node_modules/yaml": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.3.tgz", - "integrity": "sha512-zw0VAJxgeZ6+++/su5AFoqBbZbrEakwu+X0M5HmcwUiBL7AzcuPKjj5we4xfQLp78LkEMpD0cOnUhmgOVy3KdQ==", + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", + "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", "dev": true, "engines": { "node": ">= 14" diff --git a/package.json b/package.json index 54f56eb..3d8fef4 100644 --- a/package.json +++ b/package.json @@ -49,27 +49,27 @@ "devDependencies": { "@changesets/changelog-github": "^0.4.8", "@changesets/cli": "^2.26.2", - "@marko/compiler": "^5.33.7", - "@marko/vite": "^4.0.0", + "@marko/compiler": "^5.33.8", + "@marko/vite": "^4.0.2", "@playwright/test": "^1.39.0", "@types/estree": "^1.0.5", "@types/node": "^20.9.0", "@types/serve-handler": "^6.1.4", - "@typescript-eslint/eslint-plugin": "^6.10.0", - "@typescript-eslint/parser": "^6.10.0", + "@typescript-eslint/eslint-plugin": "^6.11.0", + "@typescript-eslint/parser": "^6.11.0", "c8": "^8.0.1", "eslint": "^8.53.0", "eslint-config-prettier": "^9.0.0", "eslint-import-resolver-typescript": "^3.6.1", "eslint-plugin-import": "^2.29.0", "husky": "^8.0.3", - "lint-staged": "^15.0.2", - "marko": "^5.31.17", + "lint-staged": "^15.1.0", + "marko": "^5.31.18", "playwright": "^1.39.0", "prettier": "^3.1.0", "serve-handler": "^6.1.5", "sort-package-json": "^2.6.0", - "tsx": "^4.1.0", + "tsx": "^4.1.2", "typescript": "^5.2.2", "vite": "^4.5.0" }, diff --git a/src/index.ts b/src/index.ts index 16b507d..113fa72 100644 --- a/src/index.ts +++ b/src/index.ts @@ -2,9 +2,9 @@ import type * as _arcServer from "arc-server"; import type { Plugin } from "vite"; -import { pluginServe } from "./plugins/serve"; -import { pluginBuildWeb } from "./plugins/build-web"; import { pluginBuildSSR } from "./plugins/build-ssr"; +import { pluginBuildWeb } from "./plugins/build-web"; +import { pluginServe } from "./plugins/serve"; import { type Options, getInternalPluginOptions } from "./utils/options"; export { createFlagSets, hasFlags } from "./utils/flags"; @@ -29,3 +29,12 @@ declare module "arc-server" { body?: string; }; } + +declare module "rollup" { + interface CustomPluginOptions { + arcSourceId?: string; + arcSourceCode?: string; + arcScanIds?: string[]; + arcFS?: typeof import("fs"); + } +} diff --git a/src/plugins/build-ssr.ts b/src/plugins/build-ssr.ts index 4d27a4a..7180c76 100644 --- a/src/plugins/build-ssr.ts +++ b/src/plugins/build-ssr.ts @@ -1,16 +1,22 @@ import path from "path"; import type { Plugin } from "vite"; -import { type InternalPluginOptions } from "../utils/options"; -import { type Matches, clearCache, getMatches } from "../utils/matches"; -import { indexToId } from "../utils/index-to-id"; +import { getArcFS } from "../utils/arc-fs"; +import { ensureArcPluginIsFirst } from "../utils/ensure-arc-plugin-is-first"; import { decodeFileName, encodeFileName } from "../utils/filename-encoding"; +import { indexToId } from "../utils/index-to-id"; import { isCssFile } from "../utils/is-css-file"; +import { type Matches, clearCache } from "../utils/matches"; +import { type InternalPluginOptions } from "../utils/options"; import { toPosix } from "../utils/to-posix"; -import { ensureArcPluginIsFirst } from "../utils/ensure-arc-plugin-is-first"; +import { + decodeArcVirtualMatch, + getVirtualMatches, + isArcVirtualMatch, +} from "../utils/virtual-matches"; const virtualArcServerModuleId = "\0arc-server-virtual"; const arcPrefix = "\0arc-"; -const arcSuffix = ".js"; +const arcSuffix = ".mjs"; const arcProxyPrefix = `${arcPrefix}proxy:`; // TODO: with some tweaks this plugin might work in a test env. @@ -43,18 +49,28 @@ export function pluginBuildSSR({ }, async resolveId(source, importer, options) { if (importer) { - if (source === "arc-server") { - return importer === virtualArcServerModuleId - ? this.resolve(source, undefined, options) - : { id: virtualArcServerModuleId }; + switch (source) { + case "arc-server": + return importer === virtualArcServerModuleId + ? this.resolve(source, undefined, options) + : { id: virtualArcServerModuleId }; + case "arc-server/proxy": + return null; } - if (isArcProxyId(source)) { + if (isArcId(source)) { return source; } - if (isArcProxyId(importer)) { - if (source === "arc-server/proxy") return null; + if (isArcId(importer)) { + if (isArcVirtualMatch(importer)) { + return this.resolve( + source, + this.getModuleInfo(importer)?.meta.arcSourceId, + options, + ); + } + return source; } @@ -62,16 +78,12 @@ export function pluginBuildSSR({ ...options, skipSelf: true, }); - if (resolved) { - const { id } = resolved; - if (path.isAbsolute(id)) { - const matches = getMatches(id, flagSets); - if (matches) { - adaptiveMatchesForId.set(id, matches); - return { - id: encodeArcProxyId(id), - }; - } + if (resolved && !resolved.external) { + const matches = await getVirtualMatches(this, flagSets, resolved); + if (matches) { + const { id } = resolved; + adaptiveMatchesForId.set(id, matches); + return { id: encodeArcProxyId(id) }; } } @@ -115,7 +127,7 @@ function partsToString(parts, base, injectAttrs) { }; } - if (isArcProxyId(arcProxyPrefix)) { + if (isArcProxyId(id)) { id = decodeArcProxyId(id); const adaptiveMatches = adaptiveMatchesForId.get(id); if (adaptiveMatches) { @@ -178,7 +190,6 @@ function partsToString(parts, base, injectAttrs) { code += "export default _.default;\n"; } } else { - syntheticNamedExports = true; code += `export default ${proxyCode}.default;\n`; } } @@ -193,6 +204,18 @@ function partsToString(parts, base, injectAttrs) { }; } } + } else if (isArcVirtualMatch(id)) { + const [arcSourceId, flagSet] = decodeArcVirtualMatch(id); + const { meta, moduleSideEffects, syntheticNamedExports } = + this.getModuleInfo(arcSourceId)!; + const code = meta.arcSourceCode as string; + const arcFS = getArcFS(flagSet); + return { + code, + moduleSideEffects, + syntheticNamedExports, + meta: { ...meta, arcSourceId, arcFS }, + }; } return null; @@ -234,6 +257,10 @@ function partsToString(parts, base, injectAttrs) { }; } +function isArcId(id: string) { + return id.startsWith(arcPrefix); +} + function isArcProxyId(id: string) { return id.startsWith(arcProxyPrefix); } diff --git a/src/plugins/build-web.ts b/src/plugins/build-web.ts index 0003577..ee6de66 100644 --- a/src/plugins/build-web.ts +++ b/src/plugins/build-web.ts @@ -1,19 +1,25 @@ -import path from "path"; import { promises as fs } from "fs"; +import path from "path"; import type * as estree from "estree"; import type { Plugin } from "vite"; -import { type InternalPluginOptions } from "../utils/options"; -import { type Matches, getMatches } from "../utils/matches"; +import { getArcFS } from "../utils/arc-fs"; +import { ensureArcPluginIsFirst } from "../utils/ensure-arc-plugin-is-first"; +import { decodeFileName, encodeFileName } from "../utils/filename-encoding"; import { type FlagSet, compareFlaggedObject, hasFlags } from "../utils/flags"; import { indexToId } from "../utils/index-to-id"; -import { decodeFileName, encodeFileName } from "../utils/filename-encoding"; import { isCssFile } from "../utils/is-css-file"; import { type DocManifest, generateDocManifest, generateInputDoc, } from "../utils/manifest"; -import { ensureArcPluginIsFirst } from "../utils/ensure-arc-plugin-is-first"; +import { type Matches } from "../utils/matches"; +import { type InternalPluginOptions } from "../utils/options"; +import { + decodeArcVirtualMatch, + getVirtualMatches, + isArcVirtualMatch, +} from "../utils/virtual-matches"; const arcPrefix = "\0arc-"; const arcJsSuffix = ".mjs"; @@ -60,7 +66,19 @@ export function pluginBuildWeb({ }, async resolveId(source, importer, options) { if (importer) { - if (isArcId(source) || isArcId(importer)) { + if (isArcId(source)) { + return source; + } + + if (isArcId(importer)) { + if (isArcVirtualMatch(importer)) { + return this.resolve( + source, + this.getModuleInfo(importer)?.meta.arcSourceId, + options, + ); + } + return source; } @@ -68,28 +86,22 @@ export function pluginBuildWeb({ ...options, skipSelf: true, }); - if (resolved) { - if (path.isAbsolute(resolved.id)) { - const matches = getMatches(resolved.id, flagSets); - if (matches) { - adaptiveMatchesForId.set(resolved.id, matches); - - const adaptiveImportsForImporter = - adaptiveImporters.get(importer); - if (adaptiveImportsForImporter) { - adaptiveImportsForImporter.set(source, resolved.id); - } else { - adaptiveImporters.set( - importer, - new Map([[source, resolved.id]]), - ); - } + if (resolved && !resolved.external) { + const matches = await getVirtualMatches(this, flagSets, resolved); + if (matches) { + const { id } = resolved; + adaptiveMatchesForId.set(id, matches); - return { - id: encodeArcProxyId(resolved.id), - }; + const adaptiveImportsForImporter = + adaptiveImporters.get(importer); + if (adaptiveImportsForImporter) { + adaptiveImportsForImporter.set(source, id); + } else { + adaptiveImporters.set(importer, new Map([[source, id]])); } + + return { id: encodeArcProxyId(id) }; } } @@ -296,8 +308,7 @@ export function pluginBuildWeb({ code += `export default ${arcId}.default;\n`; } } else { - syntheticNamedExports = true; - code += `export default ${runtimeId}.${arcId};\n`; + code += `export default ${runtimeId}.${arcId}.default;\n`; } } } else { @@ -337,6 +348,18 @@ export function pluginBuildWeb({ };\n`, moduleSideEffects: "no-treeshake", }; + } else if (isArcVirtualMatch(id)) { + const [arcSourceId, flagSet] = decodeArcVirtualMatch(id); + const { meta, moduleSideEffects, syntheticNamedExports } = + this.getModuleInfo(arcSourceId)!; + const code = meta.arcSourceCode as string; + const arcFS = getArcFS(flagSet); + return { + code, + moduleSideEffects, + syntheticNamedExports, + meta: { ...meta, arcSourceId, arcFS }, + }; } return null; @@ -461,7 +484,9 @@ export function pluginBuildWeb({ (adaptiveImport === adaptedImport ? encodeFileName(adaptiveImport) : `${encodeFileName(adaptiveImport)},${encodeFileName( - path.relative(path.dirname(adaptiveImport), adaptedImport), + adaptedImport[0] === "\0" + ? adaptedImport + : path.relative(path.dirname(adaptiveImport), adaptedImport), )}`) + arcJsSuffix }`; } @@ -509,10 +534,12 @@ function decodeArcInitId(id: string) { } const adaptiveImport = decodeFileName(id.slice(prefixEnd, sepStart)); - const adaptedImport = path.join( - adaptiveImport, - "..", - decodeFileName(id.slice(sepStart + 1, -arcJsSuffix.length)), + const relativeAdaptedImport = decodeFileName( + id.slice(sepStart + 1, -arcJsSuffix.length), ); + const adaptedImport = + relativeAdaptedImport[0] === "\0" + ? relativeAdaptedImport + : path.join(adaptiveImport, "..", relativeAdaptedImport); return [adaptiveImport, adaptedImport]; } diff --git a/src/plugins/serve.ts b/src/plugins/serve.ts index 8d676e6..10605fa 100644 --- a/src/plugins/serve.ts +++ b/src/plugins/serve.ts @@ -1,10 +1,12 @@ +import fs from "fs"; +import { createRequire, syncBuiltinESMExports } from "module"; import path from "path"; -import { createRequire } from "module"; import type { Plugin } from "vite"; -import { type InternalPluginOptions } from "../utils/options"; -import { getMatches } from "../utils/matches"; -import { type FlagSet, hasFlags } from "../utils/flags"; +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"; // TODO: support forced flagset for build plugins @@ -13,6 +15,7 @@ export function pluginServe({ forceFlagSet, }: InternalPluginOptions): Plugin { const flagSet = forceFlagSet?.length ? forceFlagSet : undefined; + let restoreFS: ReturnType | undefined; return { name: "arc-vite:serve", enforce: "pre", @@ -29,6 +32,8 @@ export function pluginServe({ if (!flagSet) return; + syncBuiltinESMExports(); + restoreFS = patchFS(flagSet, fs); ensureArcPluginIsFirst(config.plugins!); config.cacheDir = path.resolve( `node_modules/.vite/arc/${flagSet.join(".")}`, @@ -85,6 +90,13 @@ export function pluginServe({ return null; }, + buildEnd() { + if (restoreFS) { + restoreFS(); + syncBuiltinESMExports(); + restoreFS = undefined; + } + }, }; } diff --git a/src/tests/basic.test.ts b/src/tests/basic.test.ts index a15edd2..bd8abea 100644 --- a/src/tests/basic.test.ts +++ b/src/tests/basic.test.ts @@ -1,10 +1,10 @@ -import url from "node:url"; import path from "node:path"; import * as t from "node:test"; -import { type Page } from "playwright"; +import url from "node:url"; import { expect } from "@playwright/test"; -import { getPage } from "./utils/get-page"; +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( diff --git a/src/tests/fixtures/basic/src/entry-server.ts b/src/tests/fixtures/basic/src/entry-server.ts index 09ede5d..e0c6e18 100644 --- a/src/tests/fixtures/basic/src/entry-server.ts +++ b/src/tests/fixtures/basic/src/entry-server.ts @@ -1,7 +1,7 @@ -import url from "url"; -import path from "path"; import { promises as fs } from "fs"; import type { IncomingMessage } from "http"; +import path from "path"; +import url from "url"; import { getAssets, withFlags } from "arc-server"; import renderApp from "./entry-web"; diff --git a/src/tests/fixtures/marko-scan-files/config.ts b/src/tests/fixtures/marko-scan-files/config.ts new file mode 100644 index 0000000..55964e7 --- /dev/null +++ b/src/tests/fixtures/marko-scan-files/config.ts @@ -0,0 +1,18 @@ +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 new file mode 100644 index 0000000..bc6abcf --- /dev/null +++ b/src/tests/fixtures/marko-scan-files/src/components/app.marko @@ -0,0 +1,5 @@ + +

+ 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 new file mode 100644 index 0000000..20fe75e --- /dev/null +++ b/src/tests/fixtures/marko-scan-files/src/components/maybe-stateful.marko @@ -0,0 +1,5 @@ +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 new file mode 100644 index 0000000..bfc8d30 --- /dev/null +++ b/src/tests/fixtures/marko-scan-files/src/components/maybe-stateful[mobile].marko @@ -0,0 +1,21 @@ +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 new file mode 100644 index 0000000..b838e02 --- /dev/null +++ b/src/tests/fixtures/marko-scan-files/src/entry-server.ts @@ -0,0 +1,11 @@ +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 new file mode 100644 index 0000000..35a0b0b --- /dev/null +++ b/src/tests/fixtures/marko-scan-files/src/index.marko @@ -0,0 +1,11 @@ + + + + + + Document + + + + + diff --git a/src/tests/fixtures/marko/config.ts b/src/tests/fixtures/marko/config.ts index cc01530..55964e7 100644 --- a/src/tests/fixtures/marko/config.ts +++ b/src/tests/fixtures/marko/config.ts @@ -1,5 +1,5 @@ -import { defineConfig } from "vite"; import markoVite from "@marko/vite"; +import { defineConfig } from "vite"; import arcVite from "../../.."; export default () => defineConfig({ diff --git a/src/tests/marko-scan-files.test.ts b/src/tests/marko-scan-files.test.ts new file mode 100644 index 0000000..3519271 --- /dev/null +++ b/src/tests/marko-scan-files.test.ts @@ -0,0 +1,89 @@ +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 page.pause(); + 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/tests/marko.test.ts b/src/tests/marko.test.ts index f344bc1..c042b7e 100644 --- a/src/tests/marko.test.ts +++ b/src/tests/marko.test.ts @@ -1,10 +1,10 @@ -import url from "node:url"; import path from "node:path"; import * as t from "node:test"; -import { type Page } from "playwright"; +import url from "node:url"; import { expect } from "@playwright/test"; -import { getPage } from "./utils/get-page"; +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( diff --git a/src/tests/utils/dev-server.ts b/src/tests/utils/dev-server.ts index 7a70b81..df49d93 100644 --- a/src/tests/utils/dev-server.ts +++ b/src/tests/utils/dev-server.ts @@ -1,6 +1,7 @@ import events from "node:events"; import type { IncomingMessage } from "node:http"; import * as vite from "vite"; +import { getPort } from "./get-port"; export async function createDevServer(fixtureDir: string) { const getConfig: () => vite.UserConfig = ( @@ -13,6 +14,7 @@ export async function createDevServer(fixtureDir: string) { configFile: false, server: { middlewareMode: true, + hmr: { port: await getPort() }, // avoid port conflict by picking a random hmr port }, }), ); diff --git a/src/tests/utils/get-page.ts b/src/tests/utils/get-page.ts index 3060c54..3760150 100644 --- a/src/tests/utils/get-page.ts +++ b/src/tests/utils/get-page.ts @@ -1,12 +1,6 @@ -import net from "node:net"; import { after } from "node:test"; import * as playwright from "playwright"; - -// https://github.com/esbuild-kit/tsx/issues/113 -const { toString } = Function.prototype; -Function.prototype.toString = function () { - return toString.call(this).replace(/\b__name\(([^,]+),[^)]+\)/g, "$1"); -}; +import { getPort } from "./get-port"; export type TestContext = (typeof import("node:test"))["test"] extends ( fn: (t: infer T) => any, @@ -23,16 +17,3 @@ export async function getPage(t: TestContext | typeof import("node:test")) { t.after(() => page.close()); return { page, port }; } - -function getPort() { - return new Promise((resolve, reject) => { - const server = net.createServer(); - server - .unref() - .on("error", reject) - .listen(0, () => { - const { port } = server.address() as net.AddressInfo; - server.close(() => resolve(port)); - }); - }); -} diff --git a/src/tests/utils/get-port.ts b/src/tests/utils/get-port.ts new file mode 100644 index 0000000..15c04b5 --- /dev/null +++ b/src/tests/utils/get-port.ts @@ -0,0 +1,13 @@ +import net from "net"; +export function getPort() { + return new Promise((resolve, reject) => { + const server = net.createServer(); + server + .unref() + .on("error", reject) + .listen(0, () => { + const { port } = server.address() as net.AddressInfo; + server.close(() => resolve(port)); + }); + }); +} diff --git a/src/tests/utils/prod-server.ts b/src/tests/utils/prod-server.ts index ef8ed8a..0213cbd 100644 --- a/src/tests/utils/prod-server.ts +++ b/src/tests/utils/prod-server.ts @@ -1,8 +1,8 @@ -import path from "node:path"; -import http, { type IncomingMessage } from "node:http"; import events from "node:events"; -import * as vite from "vite"; +import http, { type IncomingMessage } from "node:http"; +import path from "node:path"; import serve from "serve-handler"; +import * as vite from "vite"; export async function createProdServer(fixtureDir: string) { const getConfig: () => vite.UserConfig = ( diff --git a/src/utils/arc-fs.ts b/src/utils/arc-fs.ts new file mode 100644 index 0000000..e86fb18 --- /dev/null +++ b/src/utils/arc-fs.ts @@ -0,0 +1,131 @@ +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/flags.ts b/src/utils/flags.ts index 5bc0651..637ec0d 100644 --- a/src/utils/flags.ts +++ b/src/utils/flags.ts @@ -72,22 +72,24 @@ export function compareFlagSets(a: FlagSet, b: FlagSet) { export function normalizeFlagSets(rawFlagSets: string[][]): FlagSet[] { if (!rawFlagSets.length) return rawFlagSets as FlagSet[]; - - const sortedFlagSets = rawFlagSets - .map(normalizeFlagSet) - .sort(compareFlagSets); - let prev = [] as unknown as FlagSet; + const sortedFlagSets = rawFlagSets.map(normalizeFlagSet).sort(); + let prev = sortedFlagSets[0]; const uniqueFlagSets = [prev]; - for (let i = 0; i < sortedFlagSets.length; i++) { + for (let i = 1; i < sortedFlagSets.length; i++) { if (compareFlagSets(prev, sortedFlagSets[i]) !== 0) { uniqueFlagSets.push((prev = sortedFlagSets[i])); } } + + if (prev.length !== 0) { + uniqueFlagSets.push([] as unknown as FlagSet); + } + return uniqueFlagSets; } export function normalizeFlagSet(flags: string[]): FlagSet { - return [...new Set(flags)].sort(compareFlags) as FlagSet; + return [...new Set(flags)].sort() as FlagSet; } export function compareFlaggedObject( @@ -98,5 +100,5 @@ export function compareFlaggedObject( } function compareFlags(a: string, b: string) { - return a.localeCompare(b); + return a > b ? 1 : a < b ? -1 : 0; } diff --git a/src/utils/manifest.ts b/src/utils/manifest.ts index 87a7b5b..39b4080 100644 --- a/src/utils/manifest.ts +++ b/src/utils/manifest.ts @@ -1,6 +1,6 @@ -import { Parser } from "htmlparser2"; import { ElementType } from "domelementtype"; import { Comment, DomHandler, Element, Node, Text } from "domhandler"; +import { Parser } from "htmlparser2"; type Serialized = ReturnType; export type DocManifest = { diff --git a/src/utils/matches.ts b/src/utils/matches.ts index 86b16e8..85cb16e 100644 --- a/src/utils/matches.ts +++ b/src/utils/matches.ts @@ -1,3 +1,4 @@ +import fs from "fs"; import path from "path"; import Resolver from "arc-resolver"; import { @@ -9,10 +10,10 @@ import { export type Matches = { default: string; alternates: [Match, ...Match[]] }; export type Match = { flags: FlagSet; value: string }; +export const resolver = new Resolver(fs); type RawMatch = { flags: string[]; value: string }; -const resolver = new Resolver(); const hasQuery = /\?.*$/; export function getMatches( id: string, diff --git a/src/utils/read-once-persisted-store.ts b/src/utils/read-once-persisted-store.ts index 2f78e07..0f22366 100644 --- a/src/utils/read-once-persisted-store.ts +++ b/src/utils/read-once-persisted-store.ts @@ -1,6 +1,6 @@ +import { promises as fs } from "fs"; import os from "os"; import path from "path"; -import { promises as fs } from "fs"; const noop = () => {}; const tmpFile = path.join(os.tmpdir(), "vite-arc-storage.json"); diff --git a/src/utils/virtual-matches.ts b/src/utils/virtual-matches.ts new file mode 100644 index 0000000..9e0af34 --- /dev/null +++ b/src/utils/virtual-matches.ts @@ -0,0 +1,87 @@ +import path from "path"; +import type { Rollup } from "vite"; +import { decodeFileName, encodeFileName } from "./filename-encoding"; +import { normalizeFlagSets, type FlagSet } from "./flags"; +import { getMatches, type Match } from "./matches"; + +const arcPrefix = "\0arc-"; +const arcVirtualMatchPrefix = `${arcPrefix}match:`; + +export async function getVirtualMatches( + ctx: Rollup.PluginContext, + flagSets: FlagSet[], + resolved: { id: string }, +) { + const { id } = resolved; + if (path.isAbsolute(id)) { + const matches = getMatches(id, flagSets); + if (matches) return matches; + } + + const { meta } = await ctx.load(resolved); + if (Array.isArray(meta.arcScanIds)) { + if (typeof meta.arcSourceCode !== "string") { + ctx.error( + "arc-vite: when providing 'arcScanIds' you must also provide the original source code as 'arcSourceCode'.", + ); + } + const matchesFlagSets: FlagSet[] = []; + await Promise.all( + meta.arcScanIds.map(async (id) => { + 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(".")) + ); +}