perf(#10923): unwind css module class name
This commit is contained in:
parent
f8f3304164
commit
172f301df4
|
@ -0,0 +1,216 @@
|
|||
import { parse } from 'acorn';
|
||||
import { generate } from 'astring';
|
||||
import { expect, it } from 'vitest';
|
||||
import { unwindCssModuleClassName } from './rollup-plugin-unwind-css-module-class-name';
|
||||
|
||||
it('Composition API', () => {
|
||||
const ast = parse(`
|
||||
import { c as api, d as defaultStore, 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';
|
||||
|
||||
const _hoisted_1 = /* @__PURE__ */ createBaseVNode("i", { class: "ti ti-photo" }, null, -1);
|
||||
const _sfc_main = /* @__PURE__ */ defineComponent({
|
||||
__name: "index.photos",
|
||||
props: {
|
||||
user: {}
|
||||
},
|
||||
setup(__props) {
|
||||
const props = __props;
|
||||
let fetching = ref(true);
|
||||
let images = ref([]);
|
||||
function thumbnail(image) {
|
||||
return defaultStore.state.disableShowingAnimatedImages ? getStaticImageUrl(image.url) : image.thumbnailUrl;
|
||||
}
|
||||
onMounted(() => {
|
||||
const image = [
|
||||
"image/jpeg",
|
||||
"image/webp",
|
||||
"image/avif",
|
||||
"image/png",
|
||||
"image/gif",
|
||||
"image/apng",
|
||||
"image/vnd.mozilla.apng"
|
||||
];
|
||||
api("users/notes", {
|
||||
userId: props.user.id,
|
||||
fileType: image,
|
||||
excludeNsfw: defaultStore.state.nsfw !== "ignore",
|
||||
limit: 10
|
||||
}).then((notes) => {
|
||||
for (const note of notes) {
|
||||
for (const file of note.files) {
|
||||
images.value.push({
|
||||
note,
|
||||
file
|
||||
});
|
||||
}
|
||||
}
|
||||
fetching.value = false;
|
||||
});
|
||||
});
|
||||
return (_ctx, _cache) => {
|
||||
const _component_MkLoading = resolveComponent("MkLoading");
|
||||
const _component_MkA = resolveComponent("MkA");
|
||||
return openBlock(), createBlock(MkContainer, {
|
||||
"max-height": 300,
|
||||
foldable: true
|
||||
}, {
|
||||
icon: withCtx(() => [
|
||||
_hoisted_1
|
||||
]),
|
||||
header: withCtx(() => [
|
||||
createTextVNode(toDisplayString(unref(i18n).ts.images), 1)
|
||||
]),
|
||||
default: withCtx(() => [
|
||||
createBaseVNode("div", {
|
||||
class: normalizeClass(_ctx.$style.root)
|
||||
}, [
|
||||
unref(fetching) ? (openBlock(), createBlock(_component_MkLoading, { key: 0 })) : createCommentVNode("", true),
|
||||
!unref(fetching) && unref(images).length > 0 ? (openBlock(), createElementBlock("div", {
|
||||
key: 1,
|
||||
class: normalizeClass(_ctx.$style.stream)
|
||||
}, [
|
||||
(openBlock(true), createElementBlock(Fragment, null, renderList(unref(images), (image) => {
|
||||
return openBlock(), createBlock(_component_MkA, {
|
||||
key: image.note.id + image.file.id,
|
||||
class: normalizeClass(_ctx.$style.img),
|
||||
to: unref(notePage)(image.note)
|
||||
}, {
|
||||
default: withCtx(() => [
|
||||
createVNode(ImgWithBlurhash, {
|
||||
hash: image.file.blurhash,
|
||||
src: thumbnail(image.file),
|
||||
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: normalizeClass(_ctx.$style.empty)
|
||||
}, toDisplayString(unref(i18n).ts.nothing), 3)) : createCommentVNode("", true)
|
||||
], 2)
|
||||
]),
|
||||
_: 1
|
||||
});
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
const root = "xenMW";
|
||||
const stream = "xaZzf";
|
||||
const img = "xtA8t";
|
||||
const empty = "xhYKj";
|
||||
const style0 = {
|
||||
root: root,
|
||||
stream: stream,
|
||||
img: img,
|
||||
empty: empty
|
||||
};
|
||||
|
||||
const cssModules = {
|
||||
"$style": style0
|
||||
};
|
||||
const index_photos = /* @__PURE__ */ _export_sfc(_sfc_main, [["__cssModules", cssModules]]);
|
||||
|
||||
export { index_photos as default };
|
||||
`.slice(1), { sourceType: 'module' });
|
||||
unwindCssModuleClassName(ast);
|
||||
expect(generate(ast)).toBe(`
|
||||
import {c as api, d as defaultStore, 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';
|
||||
const _hoisted_1 = createBaseVNode("i", {
|
||||
class: "ti ti-photo"
|
||||
}, null, -1);
|
||||
const _sfc_main = defineComponent({
|
||||
__name: "index.photos",
|
||||
props: {
|
||||
user: {}
|
||||
},
|
||||
setup(__props) {
|
||||
const props = __props;
|
||||
let fetching = ref(true);
|
||||
let images = ref([]);
|
||||
function thumbnail(image) {
|
||||
return defaultStore.state.disableShowingAnimatedImages ? getStaticImageUrl(image.url) : image.thumbnailUrl;
|
||||
}
|
||||
onMounted(() => {
|
||||
const image = ["image/jpeg", "image/webp", "image/avif", "image/png", "image/gif", "image/apng", "image/vnd.mozilla.apng"];
|
||||
api("users/notes", {
|
||||
userId: props.user.id,
|
||||
fileType: image,
|
||||
excludeNsfw: defaultStore.state.nsfw !== "ignore",
|
||||
limit: 10
|
||||
}).then(notes => {
|
||||
for (const note of notes) {
|
||||
for (const file of note.files) {
|
||||
images.value.push({
|
||||
note,
|
||||
file
|
||||
});
|
||||
}
|
||||
}
|
||||
fetching.value = false;
|
||||
});
|
||||
});
|
||||
return (_ctx, _cache) => {
|
||||
const _component_MkLoading = resolveComponent("MkLoading");
|
||||
const _component_MkA = resolveComponent("MkA");
|
||||
return (openBlock(), createBlock(MkContainer, {
|
||||
"max-height": 300,
|
||||
foldable: true
|
||||
}, {
|
||||
icon: withCtx(() => [_hoisted_1]),
|
||||
header: withCtx(() => [createTextVNode(toDisplayString(unref(i18n).ts.images), 1)]),
|
||||
default: withCtx(() => [createBaseVNode("div", {
|
||||
class: normalizeClass("xenMW")
|
||||
}, [unref(fetching) ? (openBlock(), createBlock(_component_MkLoading, {
|
||||
key: 0
|
||||
})) : createCommentVNode("", true), !unref(fetching) && unref(images).length > 0 ? (openBlock(), createElementBlock("div", {
|
||||
key: 1,
|
||||
class: normalizeClass("xaZzf")
|
||||
}, [(openBlock(true), createElementBlock(Fragment, null, renderList(unref(images), image => {
|
||||
return (openBlock(), createBlock(_component_MkA, {
|
||||
key: image.note.id + image.file.id,
|
||||
class: normalizeClass("xtA8t"),
|
||||
to: unref(notePage)(image.note)
|
||||
}, {
|
||||
default: withCtx(() => [createVNode(ImgWithBlurhash, {
|
||||
hash: image.file.blurhash,
|
||||
src: thumbnail(image.file),
|
||||
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: normalizeClass("xhYKj")
|
||||
}, toDisplayString(unref(i18n).ts.nothing), 3)) : createCommentVNode("", true)], 2)]),
|
||||
_: 1
|
||||
}));
|
||||
};
|
||||
}
|
||||
});
|
||||
const root = "xenMW";
|
||||
const stream = "xaZzf";
|
||||
const img = "xtA8t";
|
||||
const empty = "xhYKj";
|
||||
const style0 = {
|
||||
root: root,
|
||||
stream: stream,
|
||||
img: img,
|
||||
empty: empty
|
||||
};
|
||||
const cssModules = {
|
||||
"$style": style0
|
||||
};
|
||||
const index_photos = _sfc_main;
|
||||
export {index_photos as default};
|
||||
`.slice(1));
|
||||
});
|
|
@ -0,0 +1,155 @@
|
|||
import { generate } from 'astring';
|
||||
import * as estree from 'estree';
|
||||
import { walk } from '../node_modules/estree-walker/src/index.js';
|
||||
import type * as estreeWalker from 'estree-walker';
|
||||
import type { Plugin } from 'vite';
|
||||
|
||||
export function unwindCssModuleClassName(ast: estree.Node): void {
|
||||
(walk as typeof estreeWalker.walk)(ast, {
|
||||
enter(node, parent): void {
|
||||
// FIXME: support multiple exports
|
||||
if (node.type !== 'ExportNamedDeclaration') return;
|
||||
if (node.specifiers.length !== 1) return;
|
||||
if (node.specifiers[0].local.name === '_sfc_main') return;
|
||||
if (node.specifiers[0].exported.name !== 'default') return;
|
||||
if (parent?.type !== 'Program') return;
|
||||
const endIndex = parent.body.indexOf(node);
|
||||
const previousNode = parent.body[endIndex - 1];
|
||||
if (previousNode.type !== 'VariableDeclaration') return;
|
||||
if (previousNode.declarations.length !== 1) return;
|
||||
if (previousNode.declarations[0].id.type !== 'Identifier') return;
|
||||
if (previousNode.declarations[0].id.name !== node.specifiers[0].local.name) return;
|
||||
if (previousNode.declarations[0].init?.type !== 'CallExpression') return;
|
||||
if (previousNode.declarations[0].init.callee.type !== 'Identifier') return;
|
||||
if (previousNode.declarations[0].init.callee.name !== '_export_sfc') return;
|
||||
if (previousNode.declarations[0].init.arguments.length !== 2) return;
|
||||
if (previousNode.declarations[0].init.arguments[0].type !== 'Identifier') return;
|
||||
if (previousNode.declarations[0].init.arguments[0].name !== '_sfc_main') return;
|
||||
if (previousNode.declarations[0].init.arguments[1].type !== 'ArrayExpression') return;
|
||||
if (previousNode.declarations[0].init.arguments[1].elements.length !== 1) return;
|
||||
if (previousNode.declarations[0].init.arguments[1].elements[0]?.type !== 'ArrayExpression') return;
|
||||
if (previousNode.declarations[0].init.arguments[1].elements[0].elements.length !== 2) return;
|
||||
if (previousNode.declarations[0].init.arguments[1].elements[0].elements[0]?.type !== 'Literal') return;
|
||||
if (previousNode.declarations[0].init.arguments[1].elements[0].elements[0].value !== '__cssModules') return;
|
||||
if (previousNode.declarations[0].init.arguments[1].elements[0].elements[1]?.type !== 'Identifier') return;
|
||||
const cssModuleForestName = previousNode.declarations[0].init.arguments[1].elements[0].elements[1].name;
|
||||
parent.body[endIndex - 1] = {
|
||||
type: 'VariableDeclaration',
|
||||
declarations: [
|
||||
{
|
||||
type: 'VariableDeclarator',
|
||||
id: {
|
||||
type: 'Identifier',
|
||||
name: node.specifiers[0].local.name,
|
||||
},
|
||||
init: {
|
||||
type: 'Identifier',
|
||||
name: '_sfc_main',
|
||||
},
|
||||
},
|
||||
],
|
||||
kind: 'const',
|
||||
};
|
||||
const cssModuleForestNode = 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 !== 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]];
|
||||
}));
|
||||
const sfcMain = 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 !== '_sfc_main') return false;
|
||||
return true;
|
||||
}) 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;
|
||||
const ctx = render.argument.params[0];
|
||||
if (ctx.type !== 'Identifier') return;
|
||||
if (render.argument.body.type !== 'BlockStatement') return;
|
||||
//console.dir(render, { depth: Infinity });
|
||||
for (const [key, value] of moduleForest) {
|
||||
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 [];
|
||||
if (property.key.type !== 'Identifier') return [];
|
||||
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 [[property.key.name, actualValue.declarations[0].init.value as string]];
|
||||
}));
|
||||
(walk as typeof estreeWalker.walk)(render.argument.body, {
|
||||
enter(childNode) {
|
||||
if (childNode.type !== 'MemberExpression') return;
|
||||
if (childNode.object.type !== 'MemberExpression') return;
|
||||
if (childNode.object.object.type !== 'Identifier') return;
|
||||
if (childNode.object.object.name !== ctx.name) return;
|
||||
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;
|
||||
this.replace({
|
||||
type: 'Literal',
|
||||
value: actualValue,
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
// eslint-disable-next-line import/no-default-export
|
||||
export default function pluginUnwindCssModuleClassName(): Plugin {
|
||||
return {
|
||||
name: 'UnwindCssModuleClassName',
|
||||
renderChunk(code, chunk): { code: string } {
|
||||
console.log(`=======${chunk.fileName} BEFORE=======`);
|
||||
console.log(code);
|
||||
const ast = this.parse(code) as unknown as estree.Node;
|
||||
unwindCssModuleClassName(ast);
|
||||
console.log(`=======${chunk.fileName} AFTER=======`);
|
||||
console.log(generate(ast));
|
||||
return { code: generate(ast) };
|
||||
},
|
||||
};
|
||||
}
|
|
@ -24,6 +24,7 @@
|
|||
"@vitejs/plugin-vue": "4.2.3",
|
||||
"@vue-macros/reactivity-transform": "0.3.8",
|
||||
"@vue/compiler-sfc": "3.3.4",
|
||||
"astring": "1.8.5",
|
||||
"autosize": "6.0.1",
|
||||
"broadcast-channel": "4.20.2",
|
||||
"browser-image-resizer": "github:misskey-dev/browser-image-resizer#v2.2.1-misskey.3",
|
||||
|
@ -39,6 +40,7 @@
|
|||
"cropperjs": "2.0.0-beta.2",
|
||||
"date-fns": "2.30.0",
|
||||
"escape-regexp": "0.0.1",
|
||||
"estree-walker": "^3.0.3",
|
||||
"eventemitter3": "5.0.1",
|
||||
"gsap": "3.11.5",
|
||||
"idb-keyval": "6.2.1",
|
||||
|
@ -76,6 +78,7 @@
|
|||
"vuedraggable": "next"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/types": "^7.22.4",
|
||||
"@storybook/addon-actions": "7.0.18",
|
||||
"@storybook/addon-essentials": "7.0.18",
|
||||
"@storybook/addon-interactions": "7.0.18",
|
||||
|
@ -96,6 +99,7 @@
|
|||
"@storybook/vue3-vite": "7.0.18",
|
||||
"@testing-library/jest-dom": "5.16.5",
|
||||
"@testing-library/vue": "7.0.0",
|
||||
"@types/babel__traverse": "^7.20.0",
|
||||
"@types/escape-regexp": "0.0.1",
|
||||
"@types/estree": "1.0.1",
|
||||
"@types/gulp": "4.0.10",
|
||||
|
@ -116,7 +120,7 @@
|
|||
"@typescript-eslint/parser": "5.59.5",
|
||||
"@vitest/coverage-c8": "0.31.1",
|
||||
"@vue/runtime-core": "3.3.4",
|
||||
"astring": "1.8.5",
|
||||
"acorn": "^8.8.2",
|
||||
"chokidar-cli": "3.0.0",
|
||||
"cross-env": "7.0.3",
|
||||
"cypress": "12.13.0",
|
||||
|
|
|
@ -8,6 +8,7 @@ import ReactivityTransform from '@vue-macros/reactivity-transform/vite';
|
|||
import locales from '../../locales';
|
||||
import generateDTS from '../../locales/generateDTS';
|
||||
import meta from '../../package.json';
|
||||
import pluginUnwindCssModuleClassName from './lib/rollup-plugin-unwind-css-module-class-name';
|
||||
import pluginJson5 from './vite.json5';
|
||||
|
||||
const extensions = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.json', '.json5', '.svg', '.sass', '.scss', '.css', '.vue'];
|
||||
|
@ -54,6 +55,7 @@ export function getConfig(): UserConfig {
|
|||
reactivityTransform: true,
|
||||
}),
|
||||
ReactivityTransform(),
|
||||
pluginUnwindCssModuleClassName(),
|
||||
pluginJson5(),
|
||||
...process.env.NODE_ENV === 'production'
|
||||
? [
|
||||
|
|
1409
pnpm-lock.yaml
1409
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
Loading…
Reference in a new issue