deps: Update vite to v8 (#17238)

* deps: Update vite to v8

* fix

* migrate some plugins to rolldown-based

* fix broken lockfile

* wip

* update rolldown

* override rolldown version

* perf

* spdx

* fix

* update vite to 8.0.1

* chore: rewrite rollup-plugin-unwind-css-module-class-name with MagicString

* format

* swap type definitions

* replace using MagicString

* provided magicString

* fix code style

* fix

* fix

* fix

* fix

* fix

---------

Co-authored-by: kakkokari-gtyih <67428053+kakkokari-gtyih@users.noreply.github.com>

* fix: lint fixes

* swap sass with sass-embedded

* fix lint

* fix: インライン化されたVue SFC出力に対してCSS Module定義削除が効かないのを修正

* fix

* fix: バックエンドのCSS読み込みの方法が悪いのを修正

* fix: 使用されないpreloadを削除

* fix lint [ci skip]

* Apply suggestion from @syuilo

* Add comment in pnpm-workspace.yaml [ci skip]

* update vite/rolldown

* remove magic-string

---------

Co-authored-by: cm-ayf <cm.ayf2734@gmail.com>
Co-authored-by: syuilo <4439005+syuilo@users.noreply.github.com>
This commit is contained in:
かっこかり
2026-04-01 17:05:57 +09:00
committed by GitHub
parent 5361a3819b
commit e601fcb729
16 changed files with 1287 additions and 469 deletions

View File

@@ -228,6 +228,6 @@
"pid-port": "2.1.0", "pid-port": "2.1.0",
"simple-oauth2": "5.1.0", "simple-oauth2": "5.1.0",
"supertest": "7.2.2", "supertest": "7.2.2",
"vite": "7.3.1" "vite": "8.0.2"
} }
} }

View File

@@ -3,10 +3,11 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { parseAst } from 'vite'; import { parseAst } from 'rolldown/parseAst';
import * as estreeWalker from 'estree-walker'; import * as estreeWalker from 'estree-walker';
import { assertNever, assertType } from '../utils.js'; import { assertNever, assertType } from '../utils.js';
import type { AstNode, ProgramNode } from 'rollup'; import type { ESTree as RolldownESTree } from 'rolldown/utils';
import type { AstNode } from 'rollup';
import type * as estree from 'estree'; import type * as estree from 'estree';
import type { LocaleInliner, TextModification } from '../locale-inliner.js'; import type { LocaleInliner, TextModification } from '../locale-inliner.js';
import type { Logger } from '../logger.js'; import type { Logger } from '../logger.js';
@@ -17,7 +18,7 @@ interface WalkerContext {
} }
export function collectModifications(sourceCode: string, fileName: string, fileLogger: Logger, inliner: LocaleInliner): TextModification[] { export function collectModifications(sourceCode: string, fileName: string, fileLogger: Logger, inliner: LocaleInliner): TextModification[] {
let programNode: ProgramNode; let programNode: RolldownESTree.Program;
try { try {
programNode = parseAst(sourceCode); programNode = parseAst(sourceCode);
} catch (err) { } catch (err) {
@@ -35,7 +36,8 @@ export function collectModifications(sourceCode: string, fileName: string, fileL
// 1) replace all `scripts/` path literals with locale code // 1) replace all `scripts/` path literals with locale code
// 2) replace all `localStorage.getItem("lang")` with `localeName` variable // 2) replace all `localStorage.getItem("lang")` with `localeName` variable
// 3) replace all `await window.fetch(`/assets/locales/${d}.${x}.json`).then(u=>u.json())` with `localeJson` variable // 3) replace all `await window.fetch(`/assets/locales/${d}.${x}.json`).then(u=>u.json())` with `localeJson` variable
estreeWalker.walk(programNode, { // eslint-disable-next-line @typescript-eslint/no-explicit-any
(estreeWalker.walk as any)(programNode, {
enter(this: WalkerContext, node: Node) { enter(this: WalkerContext, node: Node) {
assertType<AstNode>(node); assertType<AstNode>(node);
@@ -118,8 +120,9 @@ export function collectModifications(sourceCode: string, fileName: string, fileL
// Check if the identifier is already declared in the file. // Check if the identifier is already declared in the file.
// If it is, we may overwrite it and cause issues so we skip inlining // If it is, we may overwrite it and cause issues so we skip inlining
let isSupported = true; let isSupported = true;
estreeWalker.walk(programNode, { // eslint-disable-next-line @typescript-eslint/no-explicit-any
enter(node) { (estreeWalker.walk as any)(programNode, {
enter(node: Node) {
if (node.type === 'VariableDeclaration') { if (node.type === 'VariableDeclaration') {
assertType<estree.VariableDeclaration>(node); assertType<estree.VariableDeclaration>(node);
for (const id of node.declarations.flatMap(x => declsOfPattern(x.id))) { for (const id of node.declarations.flatMap(x => declsOfPattern(x.id))) {
@@ -145,8 +148,9 @@ export function collectModifications(sourceCode: string, fileName: string, fileL
const toSkip = new Set(); const toSkip = new Set();
toSkip.add(i18nImport); toSkip.add(i18nImport);
estreeWalker.walk(programNode, { // eslint-disable-next-line @typescript-eslint/no-explicit-any
enter(this: WalkerContext, node, parent, property) { (estreeWalker.walk as any)(programNode, {
enter(this: WalkerContext, node: Node, parent: Node | null, property: string | number | symbol | null | undefined) {
assertType<AstNode>(node); assertType<AstNode>(node);
assertType<AstNode>(parent); assertType<AstNode>(parent);
if (toSkip.has(node)) { if (toSkip.has(node)) {
@@ -379,7 +383,7 @@ type SpecifierResult =
| { type: 'specifier', localI18nIdentifier: string, importNode: estree.ImportDeclaration & AstNode } | { type: 'specifier', localI18nIdentifier: string, importNode: estree.ImportDeclaration & AstNode }
; ;
function findImportSpecifier(programNode: ProgramNode, i18nFileName: string, i18nSymbol: string): SpecifierResult { function findImportSpecifier(programNode: RolldownESTree.Program, i18nFileName: string, i18nSymbol: string): SpecifierResult {
const imports = programNode.body.filter(x => x.type === 'ImportDeclaration'); const imports = programNode.body.filter(x => x.type === 'ImportDeclaration');
const importNode = imports.find(x => x.source.value === `./${i18nFileName}`) as estree.ImportDeclaration | undefined; const importNode = imports.find(x => x.source.value === `./${i18nFileName}`) as estree.ImportDeclaration | undefined;
if (!importNode) return { type: 'no-import' }; if (!importNode) return { type: 'no-import' };

View File

@@ -17,9 +17,10 @@
"rollup": "4.60.0" "rollup": "4.60.0"
}, },
"dependencies": { "dependencies": {
"i18n": "workspace:*",
"estree-walker": "3.0.3", "estree-walker": "3.0.3",
"i18n": "workspace:*",
"magic-string": "0.30.21", "magic-string": "0.30.21",
"vite": "7.3.1" "rolldown": "1.0.0-rc.11",
"vite": "8.0.2"
} }
} }

View File

@@ -4,11 +4,11 @@
*/ */
import * as estreeWalker from 'estree-walker'; import * as estreeWalker from 'estree-walker';
import MagicString from 'magic-string'; import { RolldownMagicString } from 'rolldown';
import { assertType } from './utils.js'; import { assertType } from './utils.js';
import type { ESTree } from 'rolldown/utils';
import type { Plugin } from 'vite'; import type { Plugin } from 'vite';
import type { CallExpression, Expression, Program } from 'estree'; import type { CallExpression, Expression } from 'estree';
import type { AstNode } from 'rollup';
// This plugin transforms `unref(i18n)` to `i18n` in the code, which is useful for removing unnecessary unref calls // This plugin transforms `unref(i18n)` to `i18n` in the code, which is useful for removing unnecessary unref calls
// and helps locale inliner runs after vite build to inline the locale data into the final build. // and helps locale inliner runs after vite build to inline the locale data into the final build.
@@ -23,12 +23,13 @@ export function pluginRemoveUnrefI18n(
} = {}): Plugin { } = {}): Plugin {
return { return {
name: 'UnwindCssModuleClassName', name: 'UnwindCssModuleClassName',
renderChunk(code) { renderChunk(code, _chunk, _options, meta) {
if (!code.includes('unref(i18n)')) return null; if (!code.includes('unref(i18n)')) return null;
const ast = this.parse(code) as Program; const ast = this.parse(code);
const magicString = new MagicString(code); const magicString = meta.magicString ?? new RolldownMagicString(code);
estreeWalker.walk(ast, { // eslint-disable-next-line @typescript-eslint/no-explicit-any
enter(node) { (estreeWalker.walk as any)(ast, {
enter(node: ESTree.Node) {
if (node.type === 'CallExpression' && node.callee.type === 'Identifier' && node.callee.name === 'unref' if (node.type === 'CallExpression' && node.callee.type === 'Identifier' && node.callee.name === 'unref'
&& node.arguments.length === 1) { && node.arguments.length === 1) {
// calls to unref with single argument // calls to unref with single argument
@@ -36,18 +37,16 @@ export function pluginRemoveUnrefI18n(
if (arg.type === 'Identifier' && arg.name === i18nSymbolName) { if (arg.type === 'Identifier' && arg.name === i18nSymbolName) {
// this is unref(i18n) so replace it with i18n // this is unref(i18n) so replace it with i18n
// to replace, remove the 'unref(' and the trailing ')' // to replace, remove the 'unref(' and the trailing ')'
assertType<CallExpression & AstNode>(node); assertType<CallExpression>(node);
assertType<Expression & AstNode>(arg); assertType<Expression>(arg);
magicString.remove(node.start, arg.start); magicString.remove(node.start, arg.start);
magicString.remove(arg.end, node.end); magicString.remove(arg.end, node.end);
} }
} }
}, },
}); });
return {
code: magicString.toString(), return magicString;
map: magicString.generateMap({ hires: true }),
};
}, },
}; };
} }

View File

@@ -3,7 +3,7 @@ import url from 'node:url';
import path from 'node:path'; import path from 'node:path';
import { execa } from 'execa'; import { execa } from 'execa';
import locales from 'i18n'; import locales from 'i18n';
import { LocaleInliner } from '../frontend-builder/locale-inliner.js' import { LocaleInliner } from '../frontend-builder/locale-inliner.js';
import { createLogger } from '../frontend-builder/logger'; import { createLogger } from '../frontend-builder/logger';
// requires node 21 or later // requires node 21 or later

View File

@@ -1,7 +1,10 @@
// Original: https://github.com/rollup/plugins/tree/8835dd2aed92f408d7dc72d7cc25a9728e16face/packages/json /*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import JSON5 from 'json5'; import JSON5 from 'json5';
import { Plugin } from 'rollup'; import { Plugin } from 'vite';
import { createFilter, dataToEsm } from '@rollup/pluginutils'; import { createFilter, dataToEsm } from '@rollup/pluginutils';
import { RollupJsonOptions } from '@rollup/plugin-json'; import { RollupJsonOptions } from '@rollup/plugin-json';

View File

@@ -12,7 +12,6 @@
"dependencies": { "dependencies": {
"@discordapp/twemoji": "16.0.1", "@discordapp/twemoji": "16.0.1",
"@rollup/plugin-json": "6.1.0", "@rollup/plugin-json": "6.1.0",
"@rollup/plugin-replace": "6.0.3",
"@rollup/pluginutils": "5.3.0", "@rollup/pluginutils": "5.3.0",
"@twemoji/parser": "16.0.0", "@twemoji/parser": "16.0.0",
"@vitejs/plugin-vue": "6.0.5", "@vitejs/plugin-vue": "6.0.5",
@@ -26,11 +25,9 @@
"misskey-js": "workspace:*", "misskey-js": "workspace:*",
"punycode.js": "2.3.1", "punycode.js": "2.3.1",
"rollup": "4.60.0", "rollup": "4.60.0",
"sass": "1.98.0",
"shiki": "3.23.0", "shiki": "3.23.0",
"tinycolor2": "1.6.0", "tinycolor2": "1.6.0",
"uuid": "13.0.0", "uuid": "13.0.0",
"vite": "7.3.1",
"vue": "3.5.30" "vue": "3.5.30"
}, },
"devDependencies": { "devDependencies": {
@@ -57,8 +54,10 @@
"msw": "2.12.14", "msw": "2.12.14",
"nodemon": "3.1.14", "nodemon": "3.1.14",
"prettier": "3.8.1", "prettier": "3.8.1",
"sass-embedded": "1.98.0",
"start-server-and-test": "2.1.5", "start-server-and-test": "2.1.5",
"tsx": "4.21.0", "tsx": "4.21.0",
"vite": "8.0.2",
"vite-plugin-turbosnap": "1.0.3", "vite-plugin-turbosnap": "1.0.3",
"vue-component-type-helpers": "3.2.6", "vue-component-type-helpers": "3.2.6",
"vue-eslint-parser": "10.4.0", "vue-eslint-parser": "10.4.0",

View File

@@ -7,10 +7,10 @@ import { promises as fsp } from 'fs';
import locales from 'i18n'; import locales from 'i18n';
import meta from '../../package.json'; import meta from '../../package.json';
import packageInfo from './package.json' with { type: 'json' }; import packageInfo from './package.json' with { type: 'json' };
import pluginJson5 from './vite.json5.js'; import pluginJson5 from './lib/vite-plugin-json5.js';
import { pluginRemoveUnrefI18n } from '../frontend-builder/rollup-plugin-remove-unref-i18n'; import { pluginRemoveUnrefI18n } from '../frontend-builder/rollup-plugin-remove-unref-i18n';
const url = process.env.NODE_ENV === 'development' ? yaml.load(await fsp.readFile('../../.config/default.yml', 'utf-8')).url : null; const url = process.env.NODE_ENV === 'development' ? (yaml.load(await fsp.readFile('../../.config/default.yml', 'utf-8')) as any).url : null;
const host = url ? (new URL(url)).hostname : undefined; const host = url ? (new URL(url)).hostname : undefined;
const extensions = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.json', '.json5', '.svg', '.sass', '.scss', '.css', '.vue']; const extensions = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.json', '.json5', '.svg', '.sass', '.scss', '.css', '.vue'];
@@ -113,11 +113,6 @@ export function getConfig(): UserConfig {
} }
}, },
}, },
preprocessorOptions: {
scss: {
api: 'modern-compiler',
},
},
}, },
define: { define: {
@@ -137,7 +132,10 @@ export function getConfig(): UserConfig {
'safari16', 'safari16',
], ],
manifest: 'manifest.json', manifest: 'manifest.json',
rollupOptions: { rolldownOptions: {
experimental: {
nativeMagicString: true,
},
input: { input: {
i18n: './src/i18n.ts', i18n: './src/i18n.ts',
entry: './src/boot.ts', entry: './src/boot.ts',
@@ -145,10 +143,15 @@ export function getConfig(): UserConfig {
external: externalPackages.map(p => p.match), external: externalPackages.map(p => p.match),
preserveEntrySignatures: 'allow-extension', preserveEntrySignatures: 'allow-extension',
output: { output: {
manualChunks: { codeSplitting: {
vue: ['vue'], groups: [{
// dependencies of i18n.ts name: 'vue',
'config': ['@@/js/config.js'], test: /node_modules[\\/]vue/,
}, {
// dependencies of i18n.ts
name: 'config',
test: /@@[\\/]js[\\/]config\.js/,
}],
}, },
entryFileNames: `scripts/${localesHash}-[hash:8].js`, entryFileNames: `scripts/${localesHash}-[hash:8].js`,
chunkFileNames: `scripts/${localesHash}-[hash:8].js`, chunkFileNames: `scripts/${localesHash}-[hash:8].js`,

View File

@@ -3,15 +3,15 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { parse } from 'acorn';
import { generate } from 'astring';
import { describe, expect, it } from 'vitest'; import { describe, expect, it } from 'vitest';
import { normalizeClass, unwindCssModuleClassName } from './rollup-plugin-unwind-css-module-class-name.js'; import { normalizeClass, unwindCssModuleClassName } from './rollup-plugin-unwind-css-module-class-name.js';
import type * as estree from 'estree'; import { parseAst } from 'rolldown/parseAst';
import type { ESTree } from 'rolldown/utils';
import { RolldownMagicString } from 'rolldown';
function parseExpression(code: string): estree.Expression { function parseExpression(code: string): ESTree.Expression {
const program = parse(code, { ecmaVersion: 'latest', sourceType: 'module' }) as unknown as estree.Program; const program = parseAst(code, { sourceType: 'module' });
const statement = program.body[0] as estree.ExpressionStatement; const statement = program.body[0] as ESTree.ExpressionStatement;
return statement.expression; return statement.expression;
} }
@@ -57,7 +57,7 @@ describe(normalizeClass.name, () => {
}); });
it('Composition API (standard)', () => { it('Composition API (standard)', () => {
const ast = parse(` const code = `
import { c as api, d as store, i as i18n, aD as notePage, bN as ImgWithBlurhash, bY as getStaticImageUrl, _ as _export_sfc } from './app-!~{001}~.js'; import { c as api, d as store, i as i18n, aD as notePage, bN as ImgWithBlurhash, bY as getStaticImageUrl, _ as _export_sfc } from './app-!~{001}~.js';
import { M as MkContainer } from './MkContainer-!~{03M}~.js'; import { M as MkContainer } from './MkContainer-!~{03M}~.js';
import { b as defineComponent, a as ref, e as onMounted, z as resolveComponent, g as openBlock, h as createBlock, i as withCtx, K as createTextVNode, E as toDisplayString, u as unref, l as createBaseVNode, q as normalizeClass, B as createCommentVNode, k as createElementBlock, F as Fragment, C as renderList, A as createVNode } from './vue-!~{002}~.js'; import { b as defineComponent, a as ref, e as onMounted, z as resolveComponent, g as openBlock, h as createBlock, i as withCtx, K as createTextVNode, E as toDisplayString, u as unref, l as createBaseVNode, q as normalizeClass, B as createCommentVNode, k as createElementBlock, F as Fragment, C as renderList, A as createVNode } from './vue-!~{002}~.js';
@@ -170,17 +170,19 @@ const cssModules = {
const index_photos = /* @__PURE__ */ _export_sfc(_sfc_main, [["__cssModules", cssModules]]); const index_photos = /* @__PURE__ */ _export_sfc(_sfc_main, [["__cssModules", cssModules]]);
export { index_photos as default }; export { index_photos as default };
`.slice(1), { ecmaVersion: 'latest', sourceType: 'module' }); `.slice(1);
unwindCssModuleClassName(ast); const ast = parseAst(code, { sourceType: 'module' });
expect(generate(ast)).toBe(` const magicString = new RolldownMagicString(code);
import {c as api, d as store, i as i18n, aD as notePage, bN as ImgWithBlurhash, bY as getStaticImageUrl, _ as _export_sfc} from './app-!~{001}~.js'; unwindCssModuleClassName(ast, magicString);
import {M as MkContainer} from './MkContainer-!~{03M}~.js'; expect(magicString.toString()).toBe(
import {b as defineComponent, a as ref, e as onMounted, z as resolveComponent, g as openBlock, h as createBlock, i as withCtx, K as createTextVNode, E as toDisplayString, u as unref, l as createBaseVNode, q as normalizeClass, B as createCommentVNode, k as createElementBlock, F as Fragment, C as renderList, A as createVNode} from './vue-!~{002}~.js'; `
import { c as api, d as store, i as i18n, aD as notePage, bN as ImgWithBlurhash, bY as getStaticImageUrl, _ as _export_sfc } from './app-!~{001}~.js';
import { M as MkContainer } from './MkContainer-!~{03M}~.js';
import { b as defineComponent, a as ref, e as onMounted, z as resolveComponent, g as openBlock, h as createBlock, i as withCtx, K as createTextVNode, E as toDisplayString, u as unref, l as createBaseVNode, q as normalizeClass, B as createCommentVNode, k as createElementBlock, F as Fragment, C as renderList, A as createVNode } from './vue-!~{002}~.js';
import './photoswipe-!~{003}~.js'; import './photoswipe-!~{003}~.js';
const _hoisted_1 = createBaseVNode("i", {
class: "ti ti-photo" const _hoisted_1 = /* @__PURE__ */ createBaseVNode("i", { class: "ti ti-photo" }, null, -1);
}, null, -1); const index_photos = /* @__PURE__ */ defineComponent({
const index_photos = defineComponent({
__name: "index.photos", __name: "index.photos",
props: { props: {
user: {} user: {}
@@ -193,12 +195,20 @@ const index_photos = defineComponent({
return store.s.disableShowingAnimatedImages ? getStaticImageUrl(image.url) : image.thumbnailUrl; return store.s.disableShowingAnimatedImages ? getStaticImageUrl(image.url) : image.thumbnailUrl;
} }
onMounted(() => { onMounted(() => {
const image = ["image/jpeg", "image/webp", "image/avif", "image/png", "image/gif", "image/apng", "image/vnd.mozilla.apng"]; const image = [
"image/jpeg",
"image/webp",
"image/avif",
"image/png",
"image/gif",
"image/apng",
"image/vnd.mozilla.apng"
];
api("users/notes", { api("users/notes", {
userId: props.user.id, userId: props.user.id,
fileType: image, fileType: image,
limit: 10 limit: 10
}).then(notes => { }).then((notes) => {
for (const note of notes) { for (const note of notes) {
for (const file of note.files) { for (const file of note.files) {
images.value.push({ images.value.push({
@@ -213,60 +223,77 @@ const index_photos = defineComponent({
return (_ctx, _cache) => { return (_ctx, _cache) => {
const _component_MkLoading = resolveComponent("MkLoading"); const _component_MkLoading = resolveComponent("MkLoading");
const _component_MkA = resolveComponent("MkA"); const _component_MkA = resolveComponent("MkA");
return (openBlock(), createBlock(MkContainer, { return openBlock(), createBlock(MkContainer, {
"max-height": 300, "max-height": 300,
foldable: true foldable: true
}, { }, {
icon: withCtx(() => [_hoisted_1]), icon: withCtx(() => [
header: withCtx(() => [createTextVNode(toDisplayString(unref(i18n).ts.images), 1)]), _hoisted_1
default: withCtx(() => [createBaseVNode("div", { ]),
class: "xenMW" header: withCtx(() => [
}, [unref(fetching) ? (openBlock(), createBlock(_component_MkLoading, { createTextVNode(toDisplayString(unref(i18n).ts.images), 1)
key: 0 ]),
})) : createCommentVNode("", true), !unref(fetching) && unref(images).length > 0 ? (openBlock(), createElementBlock("div", { default: withCtx(() => [
key: 1, createBaseVNode("div", {
class: "xaZzf" class: "xenMW"
}, [(openBlock(true), createElementBlock(Fragment, null, renderList(unref(images), image => { }, [
return (openBlock(), createBlock(_component_MkA, { unref(fetching) ? (openBlock(), createBlock(_component_MkLoading, { key: 0 })) : createCommentVNode("", true),
key: image.note.id + image.file.id, !unref(fetching) && unref(images).length > 0 ? (openBlock(), createElementBlock("div", {
class: "xtA8t", key: 1,
to: unref(notePage)(image.note) class: "xaZzf"
}, { }, [
default: withCtx(() => [createVNode(ImgWithBlurhash, { (openBlock(true), createElementBlock(Fragment, null, renderList(unref(images), (image) => {
hash: image.file.blurhash, return openBlock(), createBlock(_component_MkA, {
src: thumbnail(image.file), key: image.note.id + image.file.id,
title: image.file.name class: "xtA8t",
}, null, 8, ["hash", "src", "title"])]), to: unref(notePage)(image.note)
_: 2 }, {
}, 1032, ["class", "to"])); default: withCtx(() => [
}), 128))], 2)) : createCommentVNode("", true), !unref(fetching) && unref(images).length == 0 ? (openBlock(), createElementBlock("p", { createVNode(ImgWithBlurhash, {
key: 2, hash: image.file.blurhash,
class: "xhYKj" src: thumbnail(image.file),
}, toDisplayString(unref(i18n).ts.nothing), 3)) : createCommentVNode("", true)], 2)]), title: image.file.name
}, null, 8, ["hash", "src", "title"])
]),
_: 2
}, 1032, ["class", "to"]);
}), 128))
], 2)) : createCommentVNode("", true),
!unref(fetching) && unref(images).length == 0 ? (openBlock(), createElementBlock("p", {
key: 2,
class: "xhYKj"
}, toDisplayString(unref(i18n).ts.nothing), 3)) : createCommentVNode("", true)
], 2)
]),
_: 1 _: 1
})); });
}; };
} }
}); });
const root = "xenMW"; const root = "xenMW";
const stream = "xaZzf"; const stream = "xaZzf";
const img = "xtA8t"; const img = "xtA8t";
const empty = "xhYKj"; const empty = "xhYKj";
const style0 = { const style0 = {
root: root, root: root,
stream: stream, stream: stream,
img: img, img: img,
empty: empty empty: empty
}; };
const cssModules = { const cssModules = {
"$style": style0 "$style": style0
}; };
export {index_photos as default};
`.slice(1));
export { index_photos as default };
`.slice(1),
);
}); });
it('Composition API (with `useCssModule()`)', () => { it('Composition API (with `useCssModule()`)', () => {
const ast = parse(` const code = `
import { a7 as getCurrentInstance, b as defineComponent, G as useCssModule, a1 as h, H as TransitionGroup } from './!~{002}~.js'; import { a7 as getCurrentInstance, b as defineComponent, G as useCssModule, a1 as h, H as TransitionGroup } from './!~{002}~.js';
import { d as store, aK as toast, b5 as MkAd, i as i18n, _ as _export_sfc } from './app-!~{001}~.js'; import { d as store, aK as toast, b5 as MkAd, i as i18n, _ as _export_sfc } from './app-!~{001}~.js';
@@ -437,11 +464,15 @@ const cssModules = {
const MkDateSeparatedList = /* @__PURE__ */ _export_sfc(_sfc_main, [["__cssModules", cssModules]]); const MkDateSeparatedList = /* @__PURE__ */ _export_sfc(_sfc_main, [["__cssModules", cssModules]]);
export { MkDateSeparatedList as M }; export { MkDateSeparatedList as M };
`.slice(1), { ecmaVersion: 'latest', sourceType: 'module' }); `.slice(1);
unwindCssModuleClassName(ast); const ast = parseAst(code, { sourceType: 'module' });
expect(generate(ast)).toBe(` const magicString = new RolldownMagicString(code);
import {a7 as getCurrentInstance, b as defineComponent, G as useCssModule, a1 as h, H as TransitionGroup} from './!~{002}~.js'; unwindCssModuleClassName(ast, magicString);
import {d as store, aK as toast, b5 as MkAd, i as i18n, _ as _export_sfc} from './app-!~{001}~.js'; expect(magicString.toString()).toBe(
`
import { a7 as getCurrentInstance, b as defineComponent, G as useCssModule, a1 as h, H as TransitionGroup } from './!~{002}~.js';
import { d as store, aK as toast, b5 as MkAd, i as i18n, _ as _export_sfc } from './app-!~{001}~.js';
function isDebuggerEnabled(id) { function isDebuggerEnabled(id) {
try { try {
return localStorage.getItem(\`DEBUG_\${id}\`) !== null; return localStorage.getItem(\`DEBUG_\${id}\`) !== null;
@@ -458,6 +489,7 @@ function stackTraceInstances() {
} }
return stack; return stack;
} }
const _sfc_main = defineComponent({ const _sfc_main = defineComponent({
props: { props: {
items: { items: {
@@ -485,7 +517,7 @@ const _sfc_main = defineComponent({
default: false default: false
} }
}, },
setup(props, {slots, expose}) { setup(props, { slots, expose }) {
const $style = useCssModule(); const $style = useCssModule();
function getDateText(time) { function getDateText(time) {
const date = new Date(time).getDate(); const date = new Date(time).getDate();
@@ -495,28 +527,40 @@ const _sfc_main = defineComponent({
day: date.toString() day: date.toString()
}); });
} }
if (props.items.length === 0) return; if (props.items.length === 0)
return;
const renderChildrenImpl = () => props.items.map((item, i) => { const renderChildrenImpl = () => props.items.map((item, i) => {
if (!slots || !slots.default) return; if (!slots || !slots.default)
return;
const el = slots.default({ const el = slots.default({
item item
})[0]; })[0];
if (el.key == null && item.id) el.key = item.id; if (el.key == null && item.id)
el.key = item.id;
if (i !== props.items.length - 1 && new Date(item.createdAt).getDate() !== new Date(props.items[i + 1].createdAt).getDate()) { if (i !== props.items.length - 1 && new Date(item.createdAt).getDate() !== new Date(props.items[i + 1].createdAt).getDate()) {
const separator = h("div", { const separator = h("div", {
class: $style["separator"], class: $style["separator"],
key: item.id + ":separator" key: item.id + ":separator"
}, h("p", { }, h("p", {
class: $style["date"] class: $style["date"]
}, [h("span", { }, [
class: $style["date-1"] h("span", {
}, [h("i", { class: $style["date-1"]
class: \`ti ti-chevron-up \${$style["date-1-icon"]}\` }, [
}), getDateText(item.createdAt)]), h("span", { h("i", {
class: $style["date-2"] class: \`ti ti-chevron-up \${$style["date-1-icon"]}\`
}, [getDateText(props.items[i + 1].createdAt), h("i", { }),
class: \`ti ti-chevron-down \${$style["date-2-icon"]}\` getDateText(item.createdAt)
})])])); ]),
h("span", {
class: $style["date-2"]
}, [
getDateText(props.items[i + 1].createdAt),
h("i", {
class: \`ti ti-chevron-down \${$style["date-2-icon"]}\`
})
])
]));
return [el, separator]; return [el, separator];
} else { } else {
if (props.ad && item._shouldInsertAd_) { if (props.ad && item._shouldInsertAd_) {
@@ -532,17 +576,13 @@ const _sfc_main = defineComponent({
const renderChildren = () => { const renderChildren = () => {
const children = renderChildrenImpl(); const children = renderChildrenImpl();
if (isDebuggerEnabled(6864)) { if (isDebuggerEnabled(6864)) {
const nodes = children.flatMap(node => node ?? []); const nodes = children.flatMap((node) => node ?? []);
const keys = new Set(nodes.map(node => node.key)); const keys = new Set(nodes.map((node) => node.key));
if (keys.size !== nodes.length) { if (keys.size !== nodes.length) {
const id = crypto.randomUUID(); const id = crypto.randomUUID();
const instances = stackTraceInstances(); const instances = stackTraceInstances();
toast(instances.reduce((a, c) => \`\${a} at \${c.type.name}\`, \`[DEBUG_6864 (\${id})]: \${nodes.length - keys.size} duplicated keys found\`)); toast(instances.reduce((a, c) => \`\${a} at \${c.type.name}\`, \`[DEBUG_6864 (\${id})]: \${nodes.length - keys.size} duplicated keys found\`));
console.warn({ console.warn({ id, debugId: 6864, stack: instances });
id,
debugId: 6864,
stack: instances
});
} }
} }
return children; return children;
@@ -555,45 +595,136 @@ const _sfc_main = defineComponent({
el.style.top = ""; el.style.top = "";
el.style.left = ""; el.style.left = "";
} }
return () => h(prefer.s.animation ? TransitionGroup : "div", { return () => h(
class: { prefer.s.animation ? TransitionGroup : "div",
[$style["date-separated-list"]]: true, {
[$style["date-separated-list-nogap"]]: props.noGap, class: {
[$style["reversed"]]: props.reversed, [$style["date-separated-list"]]: true,
[$style["direction-down"]]: props.direction === "down", [$style["date-separated-list-nogap"]]: props.noGap,
[$style["direction-up"]]: props.direction === "up" [$style["reversed"]]: props.reversed,
[$style["direction-down"]]: props.direction === "down",
[$style["direction-up"]]: props.direction === "up"
},
...prefer.s.animation ? {
name: "list",
tag: "div",
onBeforeLeave,
onLeaveCanceled
} : {}
}, },
...prefer.s.animation ? { { default: renderChildren }
name: "list", );
tag: "div",
onBeforeLeave,
onLeaveCanceled
} : {}
}, {
default: renderChildren
});
} }
}); });
const reversed = "xxiZh"; const reversed = "xxiZh";
const separator = "xxeDx"; const separator = "xxeDx";
const date = "xxawD"; const date = "xxawD";
const style0 = { const style0 = {
"date-separated-list": "xfKPa", "date-separated-list": "xfKPa",
"date-separated-list-nogap": "xf9zr", "date-separated-list-nogap": "xf9zr",
"direction-up": "x7AeO", "direction-up": "x7AeO",
"direction-down": "xBIqc", "direction-down": "xBIqc",
reversed: reversed, reversed: reversed,
separator: separator, separator: separator,
date: date, date: date,
"date-1": "xwtmh", "date-1": "xwtmh",
"date-1-icon": "xsNPa", "date-1-icon": "xsNPa",
"date-2": "x1xvw", "date-2": "x1xvw",
"date-2-icon": "x9ZiG" "date-2-icon": "x9ZiG"
}; };
const cssModules = { const cssModules = {
"$style": style0 "$style": style0
}; };
const MkDateSeparatedList = _export_sfc(_sfc_main, [["__cssModules", cssModules]]); const MkDateSeparatedList = /* @__PURE__ */ _export_sfc(_sfc_main, [["__cssModules", cssModules]]);
export {MkDateSeparatedList as M};
export { MkDateSeparatedList as M };
`.slice(1)); `.slice(1));
}); });
it('Composition API (inlined output)', () => {
const code = `
import { a as normalizeClass, b as defineComponent, c as _export_sfc } from './runtime.js';
const CurrentComponent = /* @__PURE__ */ _export_sfc(defineComponent({
__name: "CurrentComponent",
setup() {
return (e, n) => h("div", {
class: normalizeClass([e.$style.root, "extra"])
}, null, 2);
}
}), [["__cssModules", {
"$style": {
root: "x1234"
}
}]]);
export { CurrentComponent as default };
`.slice(1);
const ast = parseAst(code, { sourceType: 'module' });
const magicString = new RolldownMagicString(code);
unwindCssModuleClassName(ast, magicString);
const output = magicString.toString();
expect(output).toContain('class: "x1234 extra"');
expect(output).toContain('defineComponent({');
expect(output).toContain('}), []);');
expect(output).not.toContain('$style');
});
it('should keep cssModules when unresolved references remain', () => {
const code = `
import { a as normalizeClass, b as defineComponent, c as _export_sfc } from './runtime.js';
const CurrentComponent = /* @__PURE__ */ _export_sfc(defineComponent({
__name: "CurrentComponent",
setup() {
return (e, n) => h("div", {
class: normalizeClass([e.$style.root, e.$style[side]])
}, null, 2);
}
}), [["__cssModules", {
"$style": {
root: "x1234"
}
}]]);
export { CurrentComponent as default };
`.slice(1);
const ast = parseAst(code, { sourceType: 'module' });
const magicString = new RolldownMagicString(code);
unwindCssModuleClassName(ast, magicString);
const output = magicString.toString();
expect(output).toContain('e.$style[side]');
expect(output).toContain('__cssModules');
expect(output).not.toContain('}), []);');
});
it('should inline cssModules references used inside class expressions', () => {
const code = `
import { a as classHelper, b as defineComponent, c as _export_sfc } from './runtime.js';
const CurrentComponent = /* @__PURE__ */ _export_sfc(defineComponent({
__name: "CurrentComponent",
setup() {
return (e, n) => h("div", {
class: classHelper([e.$style.root, { [e.$style.main]: isActive }])
}, null, 2);
}
}), [["__cssModules", {
"$style": {
root: "x1234",
main: "x5678"
}
}]]);
export { CurrentComponent as default };
`.slice(1);
const ast = parseAst(code, { sourceType: 'module' });
const magicString = new RolldownMagicString(code);
unwindCssModuleClassName(ast, magicString);
const output = magicString.toString();
expect(output).toContain('class: classHelper(["x1234", { ["x5678"]: isActive }])');
expect(output).toContain('}), []);');
expect(output).not.toContain('$style');
});

View File

@@ -3,17 +3,16 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import { generate } from 'astring'; import * as estreeWalker from 'estree-walker';
import { walk } from '../node_modules/estree-walker/src/index.js';
import type * as estree from 'estree';
import type * as estreeWalker from 'estree-walker';
import type { Plugin } from 'vite'; import type { Plugin } from 'vite';
import type { ESTree } from 'rolldown/utils';
import { RolldownMagicString } from 'rolldown';
function isFalsyIdentifier(identifier: estree.Identifier): boolean { function isFalsyIdentifier(identifier: Extract<ESTree.Node, { type: 'Identifier' }>): boolean {
return identifier.name === 'undefined' || identifier.name === 'NaN'; return identifier.name === 'undefined' || identifier.name === 'NaN';
} }
function normalizeClassWalker(tree: estree.Node, stack: string | undefined): string | null { function normalizeClassWalker(tree: ESTree.Node, stack: string | undefined): string | null {
if (tree.type === 'Identifier') return isFalsyIdentifier(tree) ? '' : null; if (tree.type === 'Identifier') return isFalsyIdentifier(tree) ? '' : null;
if (tree.type === 'Literal') return typeof tree.value === 'string' ? tree.value : ''; if (tree.type === 'Literal') return typeof tree.value === 'string' ? tree.value : '';
if (tree.type === 'BinaryExpression') { if (tree.type === 'BinaryExpression') {
@@ -26,7 +25,7 @@ function normalizeClassWalker(tree: estree.Node, stack: string | undefined): str
if (tree.type === 'TemplateLiteral') { if (tree.type === 'TemplateLiteral') {
if (tree.expressions.some((x) => x.type !== 'Literal' && (x.type !== 'Identifier' || !isFalsyIdentifier(x)))) return null; if (tree.expressions.some((x) => x.type !== 'Literal' && (x.type !== 'Identifier' || !isFalsyIdentifier(x)))) return null;
return tree.quasis.reduce((a, c, i) => { return tree.quasis.reduce((a, c, i) => {
const v = i === tree.quasis.length - 1 ? '' : (tree.expressions[i] as Partial<estree.Literal>).value; const v = i === tree.quasis.length - 1 ? '' : (tree.expressions[i] as Partial<Extract<ESTree.Node, { type: 'Literal' }>>).value;
return a + c.value.raw + (typeof v === 'string' ? v : ''); return a + c.value.raw + (typeof v === 'string' ? v : '');
}, ''); }, '');
} }
@@ -72,44 +71,144 @@ function normalizeClassWalker(tree: estree.Node, stack: string | undefined): str
tree.type !== 'ChainExpression' && tree.type !== 'ChainExpression' &&
tree.type !== 'ConditionalExpression' && tree.type !== 'ConditionalExpression' &&
tree.type !== 'LogicalExpression' && tree.type !== 'LogicalExpression' &&
tree.type !== 'MemberExpression') { tree.type !== 'MemberExpression'
) {
console.error(stack ? `Unexpected node type: ${tree.type} (in ${stack})` : `Unexpected node type: ${tree.type}`); console.error(stack ? `Unexpected node type: ${tree.type} (in ${stack})` : `Unexpected node type: ${tree.type}`);
} }
return null; return null;
} }
export function normalizeClass(tree: estree.Node, stack?: string): string | null { export function normalizeClass(tree: ESTree.Node, stack?: string): string | null {
const walked = normalizeClassWalker(tree, stack); const walked = normalizeClassWalker(tree, stack);
return walked && walked.replace(/^\s+|\s+(?=\s)|\s+$/g, ''); return walked && walked.replace(/^\s+|\s+(?=\s)|\s+$/g, '');
} }
export function unwindCssModuleClassName(ast: estree.Node): void { function getPropertyName(node: ESTree.Node, computed: boolean): string | null {
(walk as typeof estreeWalker.walk)(ast, { if (node.type === 'Identifier') return computed ? null : node.name;
enter(node, parent): void { if (node.type === 'Literal' && typeof node.value === 'string') return node.value;
return null;
}
function getMemberPropertyName(node: ESTree.MemberExpression['property'], computed: boolean): string | null {
if (node.type === 'Identifier') return computed ? null : node.name;
if (node.type === 'Literal' && typeof node.value === 'string') return node.value;
return null;
}
function findVariableDeclaration(program: ESTree.Program, name: string): ESTree.VariableDeclaration | null {
return program.body.find((x) => {
if (x.type !== 'VariableDeclaration') return false;
if (x.declarations.length !== 1) return false;
if (x.declarations[0].id.type !== 'Identifier') return false;
return x.declarations[0].id.name === name;
}) as ESTree.VariableDeclaration | null;
}
function resolveObjectExpression(program: ESTree.Program, tree: ESTree.Expression): ESTree.ObjectExpression | null {
if (tree.type === 'ObjectExpression') return tree;
if (tree.type !== 'Identifier') return null;
const declaration = findVariableDeclaration(program, tree.name);
if (declaration?.declarations[0].init?.type !== 'ObjectExpression') return null;
return declaration.declarations[0].init;
}
function resolveComponentOptions(program: ESTree.Program, tree: ESTree.Expression): ESTree.ObjectExpression | null {
const target = tree.type === 'Identifier'
? findVariableDeclaration(program, tree.name)?.declarations[0].init ?? null
: tree;
if (target?.type === 'ObjectExpression') return target;
if (target?.type !== 'CallExpression') return null;
if (target.arguments.length !== 1) return null;
if (target.arguments[0].type !== 'ObjectExpression') return null;
return target.arguments[0];
}
function resolveModuleTree(program: ESTree.Program, tree: ESTree.Expression): Map<string, string> | null {
const objectExpression = resolveObjectExpression(program, tree);
if (objectExpression === null) return null;
return new Map(objectExpression.properties.flatMap((property) => {
if (property.type !== 'Property') return [];
const actualKey = getPropertyName(property.key, property.computed);
if (actualKey === null) return [];
if (property.value.type === 'Literal') {
return typeof property.value.value === 'string' ? [[actualKey, property.value.value]] : [];
}
if (property.value.type === 'Identifier') {
const actualValue = findVariableDeclaration(program, property.value.name);
if (actualValue?.declarations[0].init?.type !== 'Literal') return [];
return typeof actualValue.declarations[0].init.value === 'string' ? [[actualKey, actualValue.declarations[0].init.value]] : [];
}
return [];
}));
}
function resolveModuleForest(program: ESTree.Program, tree: ESTree.Expression): Map<string, Map<string, string>> | null {
const objectExpression = resolveObjectExpression(program, tree);
if (objectExpression === null) return null;
return new Map(objectExpression.properties.flatMap((property) => {
if (property.type !== 'Property') return [];
const actualKey = getPropertyName(property.key, property.computed);
if (actualKey === null) return [];
const moduleTree = resolveModuleTree(program, property.value);
return moduleTree === null ? [] : [[actualKey, moduleTree]];
}));
}
function findRenderArrow(options: ESTree.ObjectExpression): Extract<ESTree.Node, { type: 'ArrowFunctionExpression' }> | null {
const setup = options.properties.find((x) => {
if (x.type !== 'Property') return false;
return getPropertyName(x.key, x.computed) === 'setup';
}) as Extract<ESTree.Node, { type: 'Property' }> | undefined;
if (setup?.value.type !== 'FunctionExpression' && setup?.value.type !== 'ArrowFunctionExpression') return null;
if (setup.value.body == null) return null;
if (setup.value.body.type !== 'BlockStatement') return null;
const render = setup.value.body.body.find((x) => x.type === 'ReturnStatement');
if (render?.type !== 'ReturnStatement') return null;
return render.argument?.type === 'ArrowFunctionExpression' ? render.argument : null;
}
function isCssModuleAccess(node: ESTree.Node, ctxName: string, key: string): node is Extract<ESTree.Node, { type: 'MemberExpression' }> {
if (node.type !== 'MemberExpression') return false;
if (node.object.type !== 'MemberExpression') return false;
if (node.object.object.type !== 'Identifier') return false;
if (node.object.object.name !== ctxName) return false;
return getMemberPropertyName(node.object.property, node.object.computed) === key;
}
function isCssModuleReference(node: ESTree.Node, ctxName: string, key: string): node is Extract<ESTree.Node, { type: 'MemberExpression' }> {
if (!isCssModuleAccess(node, ctxName, key)) return false;
return getMemberPropertyName(node.property, node.computed) !== null;
}
function isClassProperty(node: ESTree.Node | null): node is Extract<ESTree.Node, { type: 'Property' }> {
return node?.type === 'Property' && getPropertyName(node.key, node.computed) === 'class';
}
export function unwindCssModuleClassName(ast: ESTree.Node, magicString: RolldownMagicString): void {
(estreeWalker.walk as any)(ast, {
enter(node: ESTree.Node, parent: ESTree.Node | null): void {
//#region //#region
if (parent?.type !== 'Program') return; if (parent?.type !== 'Program') return;
if (ast.type !== 'Program') return;
if (node.type !== 'VariableDeclaration') return; if (node.type !== 'VariableDeclaration') return;
if (node.declarations.length !== 1) return; if (node.declarations.length !== 1) return;
if (node.declarations[0].id.type !== 'Identifier') return; if (node.declarations[0].id.type !== 'Identifier') return;
const name = node.declarations[0].id.name; const name = node.declarations[0].id.name;
if (node.declarations[0].init?.type !== 'CallExpression') return; if (node.declarations[0].init?.type !== 'CallExpression') return;
if (node.declarations[0].init.callee.type !== 'Identifier') return;
if (node.declarations[0].init.callee.name !== '_export_sfc') return;
if (node.declarations[0].init.arguments.length !== 2) return; if (node.declarations[0].init.arguments.length !== 2) return;
if (node.declarations[0].init.arguments[0].type !== 'Identifier') return; const componentNode = node.declarations[0].init.arguments[0];
const ident = node.declarations[0].init.arguments[0].name; if (componentNode.type !== 'Identifier' && componentNode.type !== 'CallExpression' && componentNode.type !== 'ObjectExpression') return;
if (!ident.startsWith('_sfc_main')) return;
if (node.declarations[0].init.arguments[1].type !== 'ArrayExpression') return; if (node.declarations[0].init.arguments[1].type !== 'ArrayExpression') return;
if (node.declarations[0].init.arguments[1].elements.length === 0) return; if (node.declarations[0].init.arguments[1].elements.length === 0) return;
const __cssModulesIndex = node.declarations[0].init.arguments[1].elements.findIndex((x) => { const cssModulesEntry = node.declarations[0].init.arguments[1].elements.find((x) => {
if (x?.type !== 'ArrayExpression') return false; if (x?.type !== 'ArrayExpression') return false;
if (x.elements.length !== 2) return false; if (x.elements.length !== 2) return false;
if (x.elements[0]?.type !== 'Literal') return false; if (x.elements[0]?.type !== 'Literal') return false;
if (x.elements[0].value !== '__cssModules') return false; if (x.elements[0].value !== '__cssModules') return false;
if (x.elements[1]?.type !== 'Identifier') return false;
return true; return true;
}); }) as ESTree.ArrayExpression | undefined;
if (!~__cssModulesIndex) return; const __cssModulesIndex = node.declarations[0].init.arguments[1].elements.indexOf(cssModulesEntry ?? null);
if (cssModulesEntry === undefined || __cssModulesIndex < 0) return;
/* This region assumeed that the entered node looks like the following code. /* This region assumeed that the entered node looks like the following code.
* *
* ```ts * ```ts
@@ -118,21 +217,10 @@ export function unwindCssModuleClassName(ast: estree.Node): void {
*/ */
//#endregion //#endregion
//#region //#region
const cssModuleForestName = ((node.declarations[0].init.arguments[1].elements[__cssModulesIndex] as estree.ArrayExpression).elements[1] as estree.Identifier).name; const cssModuleForest = cssModulesEntry.elements[1];
const cssModuleForestNode = parent.body.find((x) => { if (cssModuleForest?.type !== 'Identifier' && cssModuleForest?.type !== 'ObjectExpression') return;
if (x.type !== 'VariableDeclaration') return false; const moduleForest = resolveModuleForest(ast, cssModuleForest);
if (x.declarations.length !== 1) return false; if (moduleForest === null) return;
if (x.declarations[0].id.type !== 'Identifier') return false;
if (x.declarations[0].id.name !== cssModuleForestName) return false;
if (x.declarations[0].init?.type !== 'ObjectExpression') return false;
return true;
}) as unknown as estree.VariableDeclaration;
const moduleForest = new Map((cssModuleForestNode.declarations[0].init as estree.ObjectExpression).properties.flatMap((property) => {
if (property.type !== 'Property') return [];
if (property.key.type !== 'Literal') return [];
if (property.value.type !== 'Identifier') return [];
return [[property.key.value as string, property.value.name as string]];
}));
/* This region collected a VariableDeclaration node in the module that looks like the following code. /* This region collected a VariableDeclaration node in the module that looks like the following code.
* *
* ```ts * ```ts
@@ -143,35 +231,13 @@ export function unwindCssModuleClassName(ast: estree.Node): void {
*/ */
//#endregion //#endregion
//#region //#region
const sfcMain = parent.body.find((x) => { const options = resolveComponentOptions(ast, componentNode);
if (x.type !== 'VariableDeclaration') return false; if (options === null) return;
if (x.declarations.length !== 1) return false; const render = findRenderArrow(options);
if (x.declarations[0].id.type !== 'Identifier') return false; if (render === null) return;
if (x.declarations[0].id.name !== ident) return false; if (render.params.length !== 2) return;
return true; const ctx = render.params[0];
}) as unknown as estree.VariableDeclaration;
if (sfcMain.declarations[0].init?.type !== 'CallExpression') return;
if (sfcMain.declarations[0].init.callee.type !== 'Identifier') return;
if (sfcMain.declarations[0].init.callee.name !== 'defineComponent') return;
if (sfcMain.declarations[0].init.arguments.length !== 1) return;
if (sfcMain.declarations[0].init.arguments[0].type !== 'ObjectExpression') return;
const setup = sfcMain.declarations[0].init.arguments[0].properties.find((x) => {
if (x.type !== 'Property') return false;
if (x.key.type !== 'Identifier') return false;
if (x.key.name !== 'setup') return false;
return true;
}) as unknown as estree.Property;
if (setup.value.type !== 'FunctionExpression') return;
const render = setup.value.body.body.find((x) => {
if (x.type !== 'ReturnStatement') return false;
return true;
}) as unknown as estree.ReturnStatement;
if (render.argument?.type !== 'ArrowFunctionExpression') return;
if (render.argument.params.length !== 2) return;
const ctx = render.argument.params[0];
if (ctx.type !== 'Identifier') return; if (ctx.type !== 'Identifier') return;
if (ctx.name !== '_ctx') return;
if (render.argument.body.type !== 'BlockStatement') return;
/* This region assumed that `sfcMain` looks like the following code. /* This region assumed that `sfcMain` looks like the following code.
* *
* ```ts * ```ts
@@ -186,33 +252,8 @@ export function unwindCssModuleClassName(ast: estree.Node): void {
* ``` * ```
*/ */
//#endregion //#endregion
for (const [key, value] of moduleForest) { for (const [key, moduleTree] of moduleForest) {
//#region //#region
const cssModuleTreeNode = parent.body.find((x) => {
if (x.type !== 'VariableDeclaration') return false;
if (x.declarations.length !== 1) return false;
if (x.declarations[0].id.type !== 'Identifier') return false;
if (x.declarations[0].id.name !== value) return false;
return true;
}) as unknown as estree.VariableDeclaration;
if (cssModuleTreeNode.declarations[0].init?.type !== 'ObjectExpression') return;
const moduleTree = new Map(cssModuleTreeNode.declarations[0].init.properties.flatMap((property) => {
if (property.type !== 'Property') return [];
const actualKey = property.key.type === 'Identifier' ? property.key.name : property.key.type === 'Literal' ? property.key.value : null;
if (typeof actualKey !== 'string') return [];
if (property.value.type === 'Literal') return [[actualKey, property.value.value as string]];
if (property.value.type !== 'Identifier') return [];
const labelledValue = property.value.name;
const actualValue = parent.body.find((x) => {
if (x.type !== 'VariableDeclaration') return false;
if (x.declarations.length !== 1) return false;
if (x.declarations[0].id.type !== 'Identifier') return false;
if (x.declarations[0].id.name !== labelledValue) return false;
return true;
}) as unknown as estree.VariableDeclaration;
if (actualValue.declarations[0].init?.type !== 'Literal') return [];
return [[actualKey, actualValue.declarations[0].init.value as string]];
}));
/* This region collected VariableDeclaration nodes in the module that looks like the following code. /* This region collected VariableDeclaration nodes in the module that looks like the following code.
* *
* ```ts * ```ts
@@ -226,17 +267,14 @@ export function unwindCssModuleClassName(ast: estree.Node): void {
*/ */
//#endregion //#endregion
//#region //#region
(walk as typeof estreeWalker.walk)(render.argument.body, { (estreeWalker.walk as any)(render.body, {
enter(childNode) { enter(childNode: ESTree.Node) {
if (childNode.type !== 'MemberExpression') return; if (!isCssModuleReference(childNode, ctx.name, key)) return;
if (childNode.object.type !== 'MemberExpression') return; const actualKey = getMemberPropertyName(childNode.property, childNode.computed);
if (childNode.object.object.type !== 'Identifier') return; if (actualKey === null) return;
if (childNode.object.object.name !== ctx.name) return; const actualValue = moduleTree.get(actualKey);
if (childNode.object.property.type !== 'Identifier') return;
if (childNode.object.property.name !== key) return;
if (childNode.property.type !== 'Identifier') return;
const actualValue = moduleTree.get(childNode.property.name);
if (actualValue === undefined) return; if (actualValue === undefined) return;
magicString.overwrite(childNode.start, childNode.end, JSON.stringify(actualValue));
this.replace({ this.replace({
type: 'Literal', type: 'Literal',
value: actualValue, value: actualValue,
@@ -276,20 +314,13 @@ export function unwindCssModuleClassName(ast: estree.Node): void {
*/ */
//#endregion //#endregion
//#region //#region
(walk as typeof estreeWalker.walk)(render.argument.body, { (estreeWalker.walk as any)(render.body, {
enter(childNode) { enter(childNode: ESTree.Node) {
if (childNode.type !== 'MemberExpression') return; if (!isCssModuleReference(childNode, ctx.name, key)) return;
if (childNode.object.type !== 'MemberExpression') return; const actualKey = getMemberPropertyName(childNode.property, childNode.computed);
if (childNode.object.object.type !== 'Identifier') return; if (actualKey === null) return;
if (childNode.object.object.name !== ctx.name) return; console.error(`Undefined style detected: ${key}.${actualKey} (in ${name})`);
if (childNode.object.property.type !== 'Identifier') return; magicString.overwrite(childNode.start, childNode.end, 'undefined');
if (childNode.object.property.name !== key) return;
if (childNode.property.type !== 'Identifier') return;
console.error(`Undefined style detected: ${key}.${childNode.property.name} (in ${name})`);
this.replace({
type: 'Identifier',
name: 'undefined',
});
}, },
}); });
/* This region replaced the reference identifier of missing class names in the render function with `undefined`, as in the following code. /* This region replaced the reference identifier of missing class names in the render function with `undefined`, as in the following code.
@@ -300,7 +331,7 @@ export function unwindCssModuleClassName(ast: estree.Node): void {
* ... * ...
* return (_ctx, _cache) => { * return (_ctx, _cache) => {
* ... * ...
* return openBlock(), createElementBlock("div", { * return openBlock(), createElementBlock('div', {
* class: normalizeClass(_ctx.$style.hoge), * class: normalizeClass(_ctx.$style.hoge),
* }, null); * }, null);
* }; * };
@@ -316,7 +347,7 @@ export function unwindCssModuleClassName(ast: estree.Node): void {
* ... * ...
* return (_ctx, _cache) => { * return (_ctx, _cache) => {
* ... * ...
* return openBlock(), createElementBlock("div", { * return openBlock(), createElementBlock('div', {
* class: normalizeClass(undefined), * class: normalizeClass(undefined),
* }, null); * }, null);
* }; * };
@@ -326,18 +357,15 @@ export function unwindCssModuleClassName(ast: estree.Node): void {
*/ */
//#endregion //#endregion
//#region //#region
(walk as typeof estreeWalker.walk)(render.argument.body, { (estreeWalker.walk as any)(render.body, {
enter(childNode) { enter(childNode: ESTree.Node, childParent: ESTree.Node | null) {
if (childNode.type !== 'CallExpression') return; if (childNode.type !== 'CallExpression') return;
if (childNode.callee.type !== 'Identifier') return;
if (childNode.callee.name !== 'normalizeClass') return;
if (childNode.arguments.length !== 1) return; if (childNode.arguments.length !== 1) return;
if (childNode.callee.type === 'Identifier' && childNode.callee.name !== 'normalizeClass' && !isClassProperty(childParent)) return;
if (childNode.callee.type !== 'Identifier' && !isClassProperty(childParent)) return;
const normalized = normalizeClass(childNode.arguments[0], name); const normalized = normalizeClass(childNode.arguments[0], name);
if (normalized === null) return; if (normalized === null) return;
this.replace({ magicString.overwrite(childNode.start, childNode.end, JSON.stringify(normalized));
type: 'Literal',
value: normalized,
});
}, },
}); });
/* This region compiled the `normalizeClass` call into a pseudo-AOT compilation, as in the following code. /* This region compiled the `normalizeClass` call into a pseudo-AOT compilation, as in the following code.
@@ -374,19 +402,34 @@ export function unwindCssModuleClassName(ast: estree.Node): void {
*/ */
//#endregion //#endregion
} }
//#region const hasRemainingCssModuleReference = Array.from(moduleForest.keys()).some((key) => {
if (node.declarations[0].init.arguments[1].elements.length === 1) { let found = false;
(walk as typeof estreeWalker.walk)(ast, { (estreeWalker.walk as any)(render.body, {
enter(childNode) { enter(childNode: ESTree.Node) {
if (childNode.type !== 'Identifier') return; if (!isCssModuleAccess(childNode, ctx.name, key)) return;
if (childNode.name !== ident) return; found = true;
this.replace({ this.skip();
type: 'Identifier',
name: node.declarations[0].id.name,
});
}, },
}); });
this.remove(); return found;
});
if (hasRemainingCssModuleReference) return;
//#region
if (node.declarations[0].init.arguments[1].elements.length === 1) {
if (componentNode.type === 'Identifier') {
(estreeWalker.walk as any)(ast, {
enter(childNode: ESTree.Node) {
if (childNode.type !== 'Identifier') return;
if (childNode.name !== componentNode.name) return;
magicString.overwrite(childNode.start, childNode.end, name);
},
});
magicString.remove(node.start, node.end);
} else {
const removeStart = cssModulesEntry.start;
const removeEnd = node.declarations[0].init.arguments[1].end - 1;
magicString.remove(removeStart, removeEnd);
}
/* NOTE: The above logic is valid as long as the following two conditions are met. /* NOTE: The above logic is valid as long as the following two conditions are met.
* *
* - the uniqueness of `ident` is kept throughout the module * - the uniqueness of `ident` is kept throughout the module
@@ -411,31 +454,10 @@ export function unwindCssModuleClassName(ast: estree.Node): void {
}); });
*/ */
} else { } else {
this.replace({ const nextElement = node.declarations[0].init.arguments[1].elements[__cssModulesIndex + 1];
type: 'VariableDeclaration', const removeStart = node.declarations[0].init.arguments[1].elements[__cssModulesIndex]!.start;
declarations: [{ const removeEnd = nextElement ? nextElement.start : node.declarations[0].init.arguments[1].end - 1;
type: 'VariableDeclarator', magicString.remove(removeStart, removeEnd);
id: {
type: 'Identifier',
name: node.declarations[0].id.name,
},
init: {
type: 'CallExpression',
callee: {
type: 'Identifier',
name: '_export_sfc',
},
arguments: [{
type: 'Identifier',
name: ident,
}, {
type: 'ArrayExpression',
elements: node.declarations[0].init.arguments[1].elements.slice(0, __cssModulesIndex).concat(node.declarations[0].init.arguments[1].elements.slice(__cssModulesIndex + 1)),
}],
},
}],
kind: 'const',
});
} }
/* This region removed the `__cssModules` reference from the second argument of `_export_sfc`, as in the following code. /* This region removed the `__cssModules` reference from the second argument of `_export_sfc`, as in the following code.
* *
@@ -474,10 +496,11 @@ export function unwindCssModuleClassName(ast: estree.Node): void {
export default function pluginUnwindCssModuleClassName(): Plugin { export default function pluginUnwindCssModuleClassName(): Plugin {
return { return {
name: 'UnwindCssModuleClassName', name: 'UnwindCssModuleClassName',
renderChunk(code): { code: string } { renderChunk(code, _chunk, _options, meta) {
const ast = this.parse(code) as unknown as estree.Node; const ast = ('ast' in meta ? meta.ast ?? this.parse(code) : this.parse(code)) as ESTree.Program;
unwindCssModuleClassName(ast); const magicString = meta.magicString ?? new RolldownMagicString(code);
return { code: generate(ast) }; unwindCssModuleClassName(ast, magicString);
return magicString;
}, },
}; };
} }

View File

@@ -13,11 +13,12 @@ import {
type LogOptions, type LogOptions,
normalizePath, normalizePath,
type Plugin, type Plugin,
type PluginOption type PluginOption,
} from 'vite'; } from 'vite';
import fs from 'node:fs'; import fs from 'node:fs';
import JSON5 from 'json5'; import JSON5 from 'json5';
import MagicString, { SourceMap } from 'magic-string'; import { RolldownMagicString } from 'rolldown';
import type { TransformResult } from 'rolldown';
import path from 'node:path' import path from 'node:path'
import { hash, toBase62 } from '../vite.config'; import { hash, toBase62 } from '../vite.config';
import { minimatch } from 'minimatch'; import { minimatch } from 'minimatch';
@@ -63,7 +64,7 @@ interface MarkerRelation {
let logger = { let logger = {
info: (msg: string, options?: LogOptions) => { }, info: (msg: string, options?: LogOptions) => { },
warn: (msg: string, options?: LogOptions) => { }, warn: (msg: string, options?: LogOptions) => { },
error: (msg: string, options?: LogErrorOptions | unknown) => { }, error: (msg: string, options?: LogErrorOptions) => { },
}; };
let loggerInitialized = false; let loggerInitialized = false;
@@ -460,9 +461,18 @@ function propertyAccessProxy(path: string[]): AccessProxy {
const i18nProxy = propertyAccessProxy(['i18n']); const i18nProxy = propertyAccessProxy(['i18n']);
export function collectFileMarkers(id: string, code: string): SearchIndexItem[] { export function collectFileMarkers(id: string, code: string | RolldownMagicString | undefined): SearchIndexItem[] {
try { try {
const { descriptor, errors } = vueSfcParse(code, { let codeStr: string;
if (typeof code === 'string') {
codeStr = code;
} else if (code != null) {
codeStr = code.toString();
} else {
throw new Error(`Code is undefined for file ${id}`);
}
const { descriptor, errors } = vueSfcParse(codeStr, {
filename: id, filename: id,
}); });
@@ -473,7 +483,8 @@ export function collectFileMarkers(id: string, code: string): SearchIndexItem[]
return extractUsageInfoFromTemplateAst(descriptor.template?.ast, id); return extractUsageInfoFromTemplateAst(descriptor.template?.ast, id);
} catch (error) { } catch (error) {
logger.error(`Error analyzing file ${id}:`, error); let _error = error instanceof Error ? error : new Error(String(error));
logger.error(`Error analyzing file ${id}:`, { error: _error });
} }
return []; return [];
@@ -481,10 +492,7 @@ export function collectFileMarkers(id: string, code: string): SearchIndexItem[]
// endregion // endregion
type TransformedCode = { type TransformedCode = Exclude<TransformResult, string>;
code: string,
map: SourceMap,
};
export class MarkerIdAssigner { export class MarkerIdAssigner {
// key: file id // key: file id
@@ -509,13 +517,12 @@ export class MarkerIdAssigner {
} }
#processImpl(id: string, code: string): TransformedCode { #processImpl(id: string, code: string): TransformedCode {
const s = new MagicString(code); // magic-string のインスタンスを作成 const s = new RolldownMagicString(code); // magic-string のインスタンスを作成
const parsed = vueSfcParse(code, { filename: id }); const parsed = vueSfcParse(code, { filename: id });
if (!parsed.descriptor.template) { if (!parsed.descriptor.template) {
return { return {
code, code, // テンプレートがない場合は元のコードを返す
map: s.generateMap({ source: id, includeContent: true }),
}; };
} }
const ast = parsed.descriptor.template.ast; // テンプレート AST を取得 const ast = parsed.descriptor.template.ast; // テンプレート AST を取得
@@ -523,8 +530,7 @@ export class MarkerIdAssigner {
if (!ast) { if (!ast) {
return { return {
code: s.toString(), // 変更後のコードを返す code,
map: s.generateMap({ source: id, includeContent: true }), // ソースマップも生成 (sourceMap: true が必要)
}; };
} }
@@ -611,7 +617,6 @@ export class MarkerIdAssigner {
return { return {
code: s.toString(), // 変更後のコードを返す code: s.toString(), // 変更後のコードを返す
map: s.generateMap({ source: id, includeContent: true }), // ソースマップも生成 (sourceMap: true が必要)
}; };
} }
@@ -642,7 +647,7 @@ export class MarkerIdAssigner {
} }
} }
// Rollup プラグインとして export // Vite プラグインとして export
export default function pluginCreateSearchIndex(options: Options): PluginOption { export default function pluginCreateSearchIndex(options: Options): PluginOption {
const assigner = new MarkerIdAssigner(); const assigner = new MarkerIdAssigner();
return [ return [

View File

@@ -1,7 +1,10 @@
// Original: https://github.com/rollup/plugins/tree/8835dd2aed92f408d7dc72d7cc25a9728e16face/packages/json /*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import JSON5 from 'json5'; import JSON5 from 'json5';
import { Plugin } from 'rollup'; import { Plugin } from 'vite';
import { createFilter, dataToEsm } from '@rollup/pluginutils'; import { createFilter, dataToEsm } from '@rollup/pluginutils';
import { RollupJsonOptions } from '@rollup/plugin-json'; import { RollupJsonOptions } from '@rollup/plugin-json';

View File

@@ -3,16 +3,16 @@
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import path from 'node:path' import path from 'node:path';
import locales from 'i18n'; import locales from 'i18n';
import type { Plugin } from 'vite';
const localesDir = path.resolve(__dirname, '../../../locales') const localesDir = path.resolve(__dirname, '../../../locales')
/** /**
* 外部ファイルを監視し、必要に応じてwebSocketでメッセージを送るViteプラグイン * 外部ファイルを監視し、必要に応じてwebSocketでメッセージを送るViteプラグイン
* @returns {import('vite').Plugin}
*/ */
export default function pluginWatchLocales() { export default function pluginWatchLocales(): Plugin {
return { return {
name: 'watch-locales', name: 'watch-locales',

View File

@@ -21,10 +21,7 @@
"@github/webauthn-json": "2.1.1", "@github/webauthn-json": "2.1.1",
"@mcaptcha/core-glue": "0.1.0-alpha-5", "@mcaptcha/core-glue": "0.1.0-alpha-5",
"@misskey-dev/browser-image-resizer": "2024.1.0", "@misskey-dev/browser-image-resizer": "2024.1.0",
"@rollup/plugin-json": "6.1.0", "@sentry/vue": "10.40.0",
"@rollup/plugin-replace": "6.0.3",
"@rollup/pluginutils": "5.3.0",
"@sentry/vue": "10.45.0",
"@syuilo/aiscript": "1.2.1", "@syuilo/aiscript": "1.2.1",
"@syuilo/aiscript-0-19-0": "npm:@syuilo/aiscript@^0.19.0", "@syuilo/aiscript-0-19-0": "npm:@syuilo/aiscript@^0.19.0",
"@twemoji/parser": "16.0.0", "@twemoji/parser": "16.0.0",
@@ -64,21 +61,20 @@
"punycode.js": "2.3.1", "punycode.js": "2.3.1",
"qr-code-styling": "1.9.2", "qr-code-styling": "1.9.2",
"qr-scanner": "1.4.2", "qr-scanner": "1.4.2",
"rollup": "4.60.0", "sanitize-html": "2.17.1",
"sanitize-html": "2.17.2",
"sass": "1.98.0",
"shiki": "3.23.0", "shiki": "3.23.0",
"textarea-caret": "3.1.0", "textarea-caret": "3.1.0",
"three": "0.183.2", "three": "0.183.2",
"throttle-debounce": "5.0.2", "throttle-debounce": "5.0.2",
"tinycolor2": "1.6.0", "tinycolor2": "1.6.0",
"v-code-diff": "1.13.1", "v-code-diff": "1.13.1",
"vite": "7.3.1",
"vue": "3.5.30", "vue": "3.5.30",
"wanakana": "5.3.1" "wanakana": "5.3.1"
}, },
"devDependencies": { "devDependencies": {
"@misskey-dev/summaly": "5.2.5", "@misskey-dev/summaly": "5.2.5",
"@rollup/plugin-json": "6.1.0",
"@rollup/pluginutils": "5.3.0",
"@storybook/addon-essentials": "8.6.18", "@storybook/addon-essentials": "8.6.18",
"@storybook/addon-interactions": "8.6.18", "@storybook/addon-interactions": "8.6.18",
"@storybook/addon-links": "10.3.3", "@storybook/addon-links": "10.3.3",
@@ -123,7 +119,6 @@
"estree-walker": "3.0.3", "estree-walker": "3.0.3",
"happy-dom": "20.8.8", "happy-dom": "20.8.8",
"intersection-observer": "0.12.2", "intersection-observer": "0.12.2",
"magic-string": "0.30.21",
"micromatch": "4.0.8", "micromatch": "4.0.8",
"minimatch": "10.2.4", "minimatch": "10.2.4",
"msw": "2.12.14", "msw": "2.12.14",
@@ -132,11 +127,14 @@
"prettier": "3.8.1", "prettier": "3.8.1",
"react": "19.2.4", "react": "19.2.4",
"react-dom": "19.2.4", "react-dom": "19.2.4",
"rolldown": "1.0.0-rc.11",
"sass-embedded": "1.98.0",
"seedrandom": "3.0.5", "seedrandom": "3.0.5",
"start-server-and-test": "2.1.5", "start-server-and-test": "2.1.5",
"storybook": "10.3.3", "storybook": "10.3.3",
"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme", "storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
"tsx": "4.21.0", "tsx": "4.21.0",
"vite": "8.0.2",
"vite-plugin-glsl": "1.5.6", "vite-plugin-glsl": "1.5.6",
"vite-plugin-turbosnap": "1.0.3", "vite-plugin-turbosnap": "1.0.3",
"vitest": "4.1.1", "vitest": "4.1.1",

View File

@@ -1,7 +1,7 @@
import path from 'path'; import path from 'path';
import pluginReplace from '@rollup/plugin-replace';
import pluginVue from '@vitejs/plugin-vue'; import pluginVue from '@vitejs/plugin-vue';
import pluginGlsl from 'vite-plugin-glsl'; import pluginGlsl from 'vite-plugin-glsl';
import { replacePlugin } from 'rolldown/plugins';
import type { UserConfig } from 'vite'; import type { UserConfig } from 'vite';
import { defineConfig } from 'vite'; import { defineConfig } from 'vite';
import * as yaml from 'js-yaml'; import * as yaml from 'js-yaml';
@@ -11,13 +11,13 @@ import locales from 'i18n';
import meta from '../../package.json'; import meta from '../../package.json';
import packageInfo from './package.json' with { type: 'json' }; import packageInfo from './package.json' with { type: 'json' };
import pluginUnwindCssModuleClassName from './lib/rollup-plugin-unwind-css-module-class-name.js'; import pluginUnwindCssModuleClassName from './lib/rollup-plugin-unwind-css-module-class-name.js';
import pluginJson5 from './vite.json5.js'; import pluginJson5 from './lib/vite-plugin-json5.js';
import type { Options as SearchIndexOptions } from './lib/vite-plugin-create-search-index.js'; import type { Options as SearchIndexOptions } from './lib/vite-plugin-create-search-index.js';
import pluginCreateSearchIndex from './lib/vite-plugin-create-search-index.js'; import pluginCreateSearchIndex from './lib/vite-plugin-create-search-index.js';
import pluginWatchLocales from './lib/vite-plugin-watch-locales.js'; import pluginWatchLocales from './lib/vite-plugin-watch-locales.js';
import { pluginRemoveUnrefI18n } from '../frontend-builder/rollup-plugin-remove-unref-i18n.js'; import { pluginRemoveUnrefI18n } from '../frontend-builder/rollup-plugin-remove-unref-i18n.js';
const url = process.env.NODE_ENV === 'development' ? yaml.load(await fsp.readFile('../../.config/default.yml', 'utf-8')).url : null; const url = process.env.NODE_ENV === 'development' ? (yaml.load(await fsp.readFile('../../.config/default.yml', 'utf-8')) as any).url : null;
const host = url ? (new URL(url)).hostname : undefined; const host = url ? (new URL(url)).hostname : undefined;
const extensions = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.json', '.json5', '.svg', '.sass', '.scss', '.css', '.vue']; const extensions = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.json', '.json5', '.svg', '.sass', '.scss', '.css', '.vue'];
@@ -121,11 +121,10 @@ export function getConfig(): UserConfig {
pluginGlsl({ minify: true }), pluginGlsl({ minify: true }),
...process.env.NODE_ENV === 'production' ...process.env.NODE_ENV === 'production'
? [ ? [
pluginReplace({ replacePlugin({
'isChromatic()': JSON.stringify(false),
}, {
preventAssignment: true, preventAssignment: true,
values: {
'isChromatic()': JSON.stringify(false),
},
}), }),
] ]
: [], : [],
@@ -154,11 +153,6 @@ export function getConfig(): UserConfig {
} }
}, },
}, },
preprocessorOptions: {
scss: {
api: 'modern-compiler',
},
},
}, },
define: { define: {
@@ -178,7 +172,10 @@ export function getConfig(): UserConfig {
'safari16', 'safari16',
], ],
manifest: 'manifest.json', manifest: 'manifest.json',
rollupOptions: { rolldownOptions: {
experimental: {
nativeMagicString: true,
},
input: { input: {
i18n: './src/i18n.ts', i18n: './src/i18n.ts',
entry: './src/_boot_.ts', entry: './src/_boot_.ts',
@@ -186,11 +183,18 @@ export function getConfig(): UserConfig {
external: externalPackages.map(p => p.match), external: externalPackages.map(p => p.match),
preserveEntrySignatures: 'allow-extension', preserveEntrySignatures: 'allow-extension',
output: { output: {
manualChunks: { codeSplitting: {
vue: ['vue'], groups: [{
photoswipe: ['photoswipe', 'photoswipe/lightbox', 'photoswipe/style.css'], name: 'vue',
// dependencies of i18n.ts test: /node_modules[\\/]vue/,
'config': ['@@/js/config.js'], }, {
name: 'photoswipe',
test: /node_modules[\\/]photoswipe/,
}, {
// dependencies of i18n.ts
name: 'config',
test: /@@[\\/]js[\\/]config\.js/,
}],
}, },
entryFileNames: `scripts/${localesHash}-[hash:8].js`, entryFileNames: `scripts/${localesHash}-[hash:8].js`,
chunkFileNames: `scripts/${localesHash}-[hash:8].js`, chunkFileNames: `scripts/${localesHash}-[hash:8].js`,

853
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff