vite-plugin-qingkuai 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,333 @@
1
+ 'use strict';
2
+
3
+ var node_path = require('node:path');
4
+ var node_fs = require('node:fs');
5
+ var node_crypto = require('node:crypto');
6
+ var sourceMapJs = require('source-map-js');
7
+ var sourcemapCodec = require('@jridgewell/sourcemap-codec');
8
+ var compiler = require('qingkuai/compiler');
9
+ var postcss = require('postcss');
10
+ var selectorParser = require('postcss-selector-parser');
11
+ var vite = require('vite');
12
+
13
+ function findFilesByName(dir, targetFileName, ignoreList) {
14
+ const result = [];
15
+ const entries = node_fs.readdirSync(dir, { withFileTypes: true });
16
+ for (const entry of entries) {
17
+ if (ignoreList.has(entry.name)) {
18
+ continue;
19
+ }
20
+ const fullPath = node_path.join(dir, entry.name);
21
+ if (entry.name === targetFileName) {
22
+ result.push(fullPath);
23
+ } else if (entry.isDirectory()) {
24
+ result.push(...findFilesByName(fullPath, targetFileName, ignoreList));
25
+ }
26
+ }
27
+ return result;
28
+ }
29
+
30
+ function offsetSourceMap(mappings, sourceIndex, preLine, preColumn) {
31
+ return sourcemapCodec.decode(mappings).map((line) => {
32
+ return line.map((segment) => {
33
+ if (segment.length === 1 || segment[1] !== sourceIndex) {
34
+ return segment;
35
+ }
36
+ if (segment[2] === preLine) {
37
+ segment[3] += preColumn;
38
+ } else {
39
+ segment[2] += preLine;
40
+ }
41
+ return segment;
42
+ });
43
+ });
44
+ }
45
+ function getSourceFile(sourcemap, line, column) {
46
+ const consumer = new sourceMapJs.SourceMapConsumer({
47
+ ...sourcemap,
48
+ version: sourcemap.version.toString()
49
+ });
50
+ return consumer.originalPositionFor({
51
+ line,
52
+ column
53
+ }).source;
54
+ }
55
+
56
+ async function attachScopeForStyleSelectors(code, hash, map, sourceFile) {
57
+ const processor = postcss([
58
+ {
59
+ postcssPlugin: "postcss-attach-scope-qingkuai",
60
+ Rule(rule) {
61
+ const { line, column } = rule.source.start;
62
+ if (getSourceFile(map, line, column) !== sourceFile) {
63
+ return;
64
+ }
65
+ rule.selector = selectorParser((selectors) => {
66
+ selectors.each((selector) => {
67
+ const index = selector.nodes.findLastIndex(({ type }) => {
68
+ return type === "class" || type === "tag" || type === "id" || type === "universal";
69
+ });
70
+ if (index !== -1) {
71
+ const lastNode = selector.nodes[index];
72
+ lastNode.parent?.insertAfter(
73
+ lastNode,
74
+ selectorParser.attribute({
75
+ attribute: `qk-${hash}`,
76
+ value: void 0,
77
+ raws: {}
78
+ })
79
+ );
80
+ }
81
+ });
82
+ }).processSync(rule.selector);
83
+ }
84
+ }
85
+ ]);
86
+ const ret = await processor.process(code, {
87
+ from: map.file,
88
+ map: {
89
+ prev: map,
90
+ annotation: false
91
+ }
92
+ });
93
+ return { code: ret.css, mappings: ret.map.toJSON().mappings };
94
+ }
95
+
96
+ function qingkuaiPlugin() {
97
+ let isDev;
98
+ let sourcemap;
99
+ let cssSourcemap;
100
+ let viteConfig;
101
+ const compileResultCache = /* @__PURE__ */ new Map();
102
+ const qingkuaiConfigurations = /* @__PURE__ */ new Map();
103
+ const qingkuaiPackageServeRE = /node_modules\/\.vite\/deps\/chunk.*$/;
104
+ const confIdentifierRE = /__qk_expose_(?:dependencies|destructions)__/g;
105
+ const styleIdRE = /^virtual:\[\d+\].*?\.qk.(?:css|s[ac]ss|less|stylus|postcss)\?\d{13}$/;
106
+ const qingkuaiPackageBuildRE = /node_modules\/qingkuai\/dist\/esm\/(?:chunks|runtime)\/\w+\.js$/;
107
+ return {
108
+ name: "qingkuai-compiler",
109
+ config(_, env) {
110
+ isDev = env.command === "serve";
111
+ },
112
+ configureServer(server) {
113
+ createQingkuaiConfigurationWatcher(server);
114
+ },
115
+ configResolved(config) {
116
+ viteConfig = config;
117
+ if (isDev) {
118
+ sourcemap = true;
119
+ cssSourcemap = !!config.css.devSourcemap;
120
+ } else {
121
+ cssSourcemap = true;
122
+ sourcemap = config.build.sourcemap !== false;
123
+ }
124
+ loadAllQingkuaiConfigurations(config.root);
125
+ },
126
+ resolveId(id, importer) {
127
+ if (styleIdRE.test(id)) {
128
+ return id;
129
+ }
130
+ if (importer?.endsWith(".qk") && !node_path.extname(id)) {
131
+ const qingkuaiConfig = getQingkuaiConfiguration(id);
132
+ return id + (qingkuaiConfig.resolveImportExtension ? ".qk" : "");
133
+ }
134
+ },
135
+ async load(id) {
136
+ if (styleIdRE.test(id)) {
137
+ const { fileId, index } = parseStyleId(id);
138
+ if (index === -1) {
139
+ return "";
140
+ }
141
+ let virtualFileName;
142
+ const compileRes = compileResultCache.get(fileId);
143
+ const style = compileRes.inputDescriptor.styles[index];
144
+ while (true) {
145
+ const hash = node_crypto.randomBytes(6).toString("hex");
146
+ virtualFileName = `${fileId}.${hash}.${style.lang}`;
147
+ if (!node_fs.existsSync(virtualFileName)) {
148
+ break;
149
+ }
150
+ }
151
+ const preprocessRes = await vite.preprocessCSS(style.code, virtualFileName, {
152
+ ...viteConfig,
153
+ css: {
154
+ ...viteConfig.css,
155
+ postcss: {
156
+ from: virtualFileName
157
+ }
158
+ }
159
+ });
160
+ if (!cssSourcemap) {
161
+ return preprocessRes.code;
162
+ }
163
+ const assertedPreprocessMap = preprocessRes.map;
164
+ const attachScopeResult = await attachScopeForStyleSelectors(
165
+ preprocessRes.code,
166
+ compileRes.hashId,
167
+ assertedPreprocessMap,
168
+ virtualFileName
169
+ );
170
+ const offsetMappings = sourcemapCodec.encode(
171
+ offsetSourceMap(
172
+ attachScopeResult.mappings,
173
+ preprocessRes.deps?.size || 0,
174
+ style.loc.start.line - 1,
175
+ style.loc.start.column - 1
176
+ )
177
+ );
178
+ return {
179
+ code: attachScopeResult.code,
180
+ map: {
181
+ version: 3,
182
+ mappings: offsetMappings,
183
+ names: assertedPreprocessMap.names,
184
+ sources: [...assertedPreprocessMap.sources.slice(0, -1), fileId]
185
+ }
186
+ };
187
+ }
188
+ },
189
+ async transform(src, id) {
190
+ const qingkuaiConfig = getQingkuaiConfiguration(id);
191
+ if (!id.endsWith(".qk")) {
192
+ if (isDev && qingkuaiPackageServeRE.test(id) || !isDev && qingkuaiPackageBuildRE.test(id)) {
193
+ const ret = src.replace(confIdentifierRE, (s) => {
194
+ switch (s) {
195
+ case "__qk_expose_dependencies__":
196
+ return JSON.stringify(!!qingkuaiConfig.exposeDependencies);
197
+ case "__qk_expose_destructions__":
198
+ return JSON.stringify(!!qingkuaiConfig.exposeDestructions);
199
+ default:
200
+ return s;
201
+ }
202
+ });
203
+ return ret;
204
+ }
205
+ return;
206
+ }
207
+ const compileRes = compiler.compile(src, {
208
+ sourcemap,
209
+ debug: isDev,
210
+ hashId: compileResultCache.get(id)?.hashId || void 0,
211
+ reserveTemplateComment: getReserveHtmlComments(qingkuaiConfig),
212
+ componentName: compiler.util.kebab2Camel(node_path.basename(id, node_path.extname(id)), true)
213
+ });
214
+ compileRes.messages.forEach(({ type, value: warning }) => {
215
+ if (type === "warning") {
216
+ this.warn(warning.message);
217
+ }
218
+ });
219
+ compileResultCache.set(id, compileRes);
220
+ const compiledCodeArr = [compileRes.code];
221
+ compileRes.inputDescriptor.styles.forEach((_, index) => {
222
+ compiledCodeArr.push(`import "virtual:[${index}]${id}.css?${Date.now()}"`);
223
+ });
224
+ const baseMap = {
225
+ version: 3,
226
+ sources: [id],
227
+ sourcesContent: [src]
228
+ };
229
+ const compiledCode = compiledCodeArr.join("\n");
230
+ if (!compileRes.inputDescriptor.script.isTS) {
231
+ if (!sourcemap) {
232
+ return compiledCode;
233
+ }
234
+ return {
235
+ code: compiledCode,
236
+ map: Object.assign(baseMap, {
237
+ mappings: compileRes.mappings
238
+ })
239
+ };
240
+ }
241
+ const esBuildCompileRes = await vite.transformWithEsbuild(
242
+ compiledCode,
243
+ id,
244
+ {
245
+ sourcemap,
246
+ loader: "ts",
247
+ target: "esnext"
248
+ },
249
+ sourcemap ? {
250
+ version: 3,
251
+ sources: [id],
252
+ sourcesContent: [src],
253
+ mappings: compileRes.mappings
254
+ } : void 0
255
+ );
256
+ if (!sourcemap) {
257
+ return esBuildCompileRes.code;
258
+ }
259
+ return {
260
+ code: esBuildCompileRes.code,
261
+ map: Object.assign(baseMap, {
262
+ mappings: esBuildCompileRes.map.mappings
263
+ })
264
+ };
265
+ }
266
+ };
267
+ function getQingkuaiConfiguration(id) {
268
+ let config = {
269
+ insertTipComments: true,
270
+ exposeDestructions: isDev,
271
+ exposeDependencies: isDev,
272
+ resolveImportExtension: true,
273
+ reserveHtmlComments: "development"
274
+ };
275
+ while (true) {
276
+ const dir = node_path.dirname(id);
277
+ if (dir === id) {
278
+ break;
279
+ }
280
+ const got = qingkuaiConfigurations.get(id = dir);
281
+ if (got) {
282
+ config = got;
283
+ break;
284
+ }
285
+ }
286
+ return config;
287
+ }
288
+ function getReserveHtmlComments(config) {
289
+ switch (config.reserveHtmlComments) {
290
+ case "all":
291
+ return true;
292
+ case "never":
293
+ return false;
294
+ case "production":
295
+ return !isDev;
296
+ default:
297
+ return isDev;
298
+ }
299
+ }
300
+ function createQingkuaiConfigurationWatcher(server) {
301
+ const watcher = server.watcher.add([".qingkuairc", "!**/node_modules/**"]);
302
+ watcher.on("unlink", (path) => qingkuaiConfigurations.delete(node_path.dirname(path)));
303
+ watcher.on("change", recordQingkuaiConfiguration);
304
+ watcher.on("add", recordQingkuaiConfiguration);
305
+ }
306
+ function loadAllQingkuaiConfigurations(workspaceDir) {
307
+ findFilesByName(workspaceDir, ".qingkuairc", /* @__PURE__ */ new Set(["node_modules"])).forEach((fileName) => {
308
+ recordQingkuaiConfiguration(fileName);
309
+ });
310
+ }
311
+ function recordQingkuaiConfiguration(fileName) {
312
+ try {
313
+ qingkuaiConfigurations.set(node_path.dirname(fileName), JSON.parse(node_fs.readFileSync(fileName, "utf-8")));
314
+ } catch {
315
+ }
316
+ }
317
+ }
318
+ function parseStyleId(id) {
319
+ const m1 = /^virtual:\[(\d+)\]/.exec(id);
320
+ const m2 = /\.[a-z]+\?\d{13}$/.exec(id);
321
+ if (!m1 || !m2) {
322
+ return {
323
+ index: -1,
324
+ fileId: ""
325
+ };
326
+ }
327
+ return {
328
+ index: parseInt(m1[1]),
329
+ fileId: id.slice(m1.index + m1[0].length, m2.index)
330
+ };
331
+ }
332
+
333
+ module.exports = qingkuaiPlugin;
@@ -0,0 +1,5 @@
1
+ import { Plugin } from 'vite';
2
+
3
+ declare function qingkuaiPlugin(): Plugin;
4
+
5
+ export { qingkuaiPlugin as default };
package/dist/index.js ADDED
@@ -0,0 +1,332 @@
1
+ import { join, basename, extname, dirname } from 'node:path';
2
+ import { readdirSync, existsSync, readFileSync } from 'node:fs';
3
+ import { randomBytes } from 'node:crypto';
4
+ import { SourceMapConsumer } from 'source-map-js';
5
+ import { decode, encode } from '@jridgewell/sourcemap-codec';
6
+ import { compile, util } from 'qingkuai/compiler';
7
+ import postcss from 'postcss';
8
+ import selectorParser from 'postcss-selector-parser';
9
+ import { transformWithEsbuild, preprocessCSS } from 'vite';
10
+
11
+ function findFilesByName(dir, targetFileName, ignoreList) {
12
+ const result = [];
13
+ const entries = readdirSync(dir, { withFileTypes: true });
14
+ for (const entry of entries) {
15
+ if (ignoreList.has(entry.name)) {
16
+ continue;
17
+ }
18
+ const fullPath = join(dir, entry.name);
19
+ if (entry.name === targetFileName) {
20
+ result.push(fullPath);
21
+ } else if (entry.isDirectory()) {
22
+ result.push(...findFilesByName(fullPath, targetFileName, ignoreList));
23
+ }
24
+ }
25
+ return result;
26
+ }
27
+
28
+ function offsetSourceMap(mappings, sourceIndex, preLine, preColumn) {
29
+ return decode(mappings).map((line) => {
30
+ return line.map((segment) => {
31
+ if (segment.length === 1 || segment[1] !== sourceIndex) {
32
+ return segment;
33
+ }
34
+ if (segment[2] === preLine) {
35
+ segment[3] += preColumn;
36
+ } else {
37
+ segment[2] += preLine;
38
+ }
39
+ return segment;
40
+ });
41
+ });
42
+ }
43
+ function getSourceFile(sourcemap, line, column) {
44
+ const consumer = new SourceMapConsumer({
45
+ ...sourcemap,
46
+ version: sourcemap.version.toString()
47
+ });
48
+ return consumer.originalPositionFor({
49
+ line,
50
+ column
51
+ }).source;
52
+ }
53
+
54
+ async function attachScopeForStyleSelectors(code, hash, map, sourceFile) {
55
+ const processor = postcss([
56
+ {
57
+ postcssPlugin: "postcss-attach-scope-qingkuai",
58
+ Rule(rule) {
59
+ const { line, column } = rule.source.start;
60
+ if (getSourceFile(map, line, column) !== sourceFile) {
61
+ return;
62
+ }
63
+ rule.selector = selectorParser((selectors) => {
64
+ selectors.each((selector) => {
65
+ const index = selector.nodes.findLastIndex(({ type }) => {
66
+ return type === "class" || type === "tag" || type === "id" || type === "universal";
67
+ });
68
+ if (index !== -1) {
69
+ const lastNode = selector.nodes[index];
70
+ lastNode.parent?.insertAfter(
71
+ lastNode,
72
+ selectorParser.attribute({
73
+ attribute: `qk-${hash}`,
74
+ value: void 0,
75
+ raws: {}
76
+ })
77
+ );
78
+ }
79
+ });
80
+ }).processSync(rule.selector);
81
+ }
82
+ }
83
+ ]);
84
+ const ret = await processor.process(code, {
85
+ from: map.file,
86
+ map: {
87
+ prev: map,
88
+ annotation: false
89
+ }
90
+ });
91
+ return { code: ret.css, mappings: ret.map.toJSON().mappings };
92
+ }
93
+
94
+ function qingkuaiPlugin() {
95
+ let isDev;
96
+ let sourcemap;
97
+ let cssSourcemap;
98
+ let viteConfig;
99
+ const compileResultCache = /* @__PURE__ */ new Map();
100
+ const qingkuaiConfigurations = /* @__PURE__ */ new Map();
101
+ const qingkuaiPackageServeRE = /node_modules\/\.vite\/deps\/chunk.*$/;
102
+ const confIdentifierRE = /__qk_expose_(?:dependencies|destructions)__/g;
103
+ const styleIdRE = /^virtual:\[\d+\].*?\.qk.(?:css|s[ac]ss|less|stylus|postcss)\?\d{13}$/;
104
+ const qingkuaiPackageBuildRE = /node_modules\/qingkuai\/dist\/esm\/(?:chunks|runtime)\/\w+\.js$/;
105
+ return {
106
+ name: "qingkuai-compiler",
107
+ config(_, env) {
108
+ isDev = env.command === "serve";
109
+ },
110
+ configureServer(server) {
111
+ createQingkuaiConfigurationWatcher(server);
112
+ },
113
+ configResolved(config) {
114
+ viteConfig = config;
115
+ if (isDev) {
116
+ sourcemap = true;
117
+ cssSourcemap = !!config.css.devSourcemap;
118
+ } else {
119
+ cssSourcemap = true;
120
+ sourcemap = config.build.sourcemap !== false;
121
+ }
122
+ loadAllQingkuaiConfigurations(config.root);
123
+ },
124
+ resolveId(id, importer) {
125
+ if (styleIdRE.test(id)) {
126
+ return id;
127
+ }
128
+ if (importer?.endsWith(".qk") && !extname(id)) {
129
+ const qingkuaiConfig = getQingkuaiConfiguration(id);
130
+ return id + (qingkuaiConfig.resolveImportExtension ? ".qk" : "");
131
+ }
132
+ },
133
+ async load(id) {
134
+ if (styleIdRE.test(id)) {
135
+ const { fileId, index } = parseStyleId(id);
136
+ if (index === -1) {
137
+ return "";
138
+ }
139
+ let virtualFileName;
140
+ const compileRes = compileResultCache.get(fileId);
141
+ const style = compileRes.inputDescriptor.styles[index];
142
+ while (true) {
143
+ const hash = randomBytes(6).toString("hex");
144
+ virtualFileName = `${fileId}.${hash}.${style.lang}`;
145
+ if (!existsSync(virtualFileName)) {
146
+ break;
147
+ }
148
+ }
149
+ const preprocessRes = await preprocessCSS(style.code, virtualFileName, {
150
+ ...viteConfig,
151
+ css: {
152
+ ...viteConfig.css,
153
+ postcss: {
154
+ from: virtualFileName
155
+ }
156
+ }
157
+ });
158
+ if (!cssSourcemap) {
159
+ return preprocessRes.code;
160
+ }
161
+ const assertedPreprocessMap = preprocessRes.map;
162
+ const attachScopeResult = await attachScopeForStyleSelectors(
163
+ preprocessRes.code,
164
+ compileRes.hashId,
165
+ assertedPreprocessMap,
166
+ virtualFileName
167
+ );
168
+ const offsetMappings = encode(
169
+ offsetSourceMap(
170
+ attachScopeResult.mappings,
171
+ preprocessRes.deps?.size || 0,
172
+ style.loc.start.line - 1,
173
+ style.loc.start.column - 1
174
+ )
175
+ );
176
+ return {
177
+ code: attachScopeResult.code,
178
+ map: {
179
+ version: 3,
180
+ mappings: offsetMappings,
181
+ names: assertedPreprocessMap.names,
182
+ sources: [...assertedPreprocessMap.sources.slice(0, -1), fileId]
183
+ }
184
+ };
185
+ }
186
+ },
187
+ async transform(src, id) {
188
+ const qingkuaiConfig = getQingkuaiConfiguration(id);
189
+ if (!id.endsWith(".qk")) {
190
+ if (isDev && qingkuaiPackageServeRE.test(id) || !isDev && qingkuaiPackageBuildRE.test(id)) {
191
+ const ret = src.replace(confIdentifierRE, (s) => {
192
+ switch (s) {
193
+ case "__qk_expose_dependencies__":
194
+ return JSON.stringify(!!qingkuaiConfig.exposeDependencies);
195
+ case "__qk_expose_destructions__":
196
+ return JSON.stringify(!!qingkuaiConfig.exposeDestructions);
197
+ default:
198
+ return s;
199
+ }
200
+ });
201
+ return ret;
202
+ }
203
+ return;
204
+ }
205
+ const compileRes = compile(src, {
206
+ sourcemap,
207
+ debug: isDev,
208
+ hashId: compileResultCache.get(id)?.hashId || void 0,
209
+ reserveTemplateComment: getReserveHtmlComments(qingkuaiConfig),
210
+ componentName: util.kebab2Camel(basename(id, extname(id)), true)
211
+ });
212
+ compileRes.messages.forEach(({ type, value: warning }) => {
213
+ if (type === "warning") {
214
+ this.warn(warning.message);
215
+ }
216
+ });
217
+ compileResultCache.set(id, compileRes);
218
+ const compiledCodeArr = [compileRes.code];
219
+ compileRes.inputDescriptor.styles.forEach((_, index) => {
220
+ compiledCodeArr.push(`import "virtual:[${index}]${id}.css?${Date.now()}"`);
221
+ });
222
+ const baseMap = {
223
+ version: 3,
224
+ sources: [id],
225
+ sourcesContent: [src]
226
+ };
227
+ const compiledCode = compiledCodeArr.join("\n");
228
+ if (!compileRes.inputDescriptor.script.isTS) {
229
+ if (!sourcemap) {
230
+ return compiledCode;
231
+ }
232
+ return {
233
+ code: compiledCode,
234
+ map: Object.assign(baseMap, {
235
+ mappings: compileRes.mappings
236
+ })
237
+ };
238
+ }
239
+ const esBuildCompileRes = await transformWithEsbuild(
240
+ compiledCode,
241
+ id,
242
+ {
243
+ sourcemap,
244
+ loader: "ts",
245
+ target: "esnext"
246
+ },
247
+ sourcemap ? {
248
+ version: 3,
249
+ sources: [id],
250
+ sourcesContent: [src],
251
+ mappings: compileRes.mappings
252
+ } : void 0
253
+ );
254
+ if (!sourcemap) {
255
+ return esBuildCompileRes.code;
256
+ }
257
+ return {
258
+ code: esBuildCompileRes.code,
259
+ map: Object.assign(baseMap, {
260
+ mappings: esBuildCompileRes.map.mappings
261
+ })
262
+ };
263
+ }
264
+ };
265
+ function getQingkuaiConfiguration(id) {
266
+ let config = {
267
+ insertTipComments: true,
268
+ exposeDestructions: isDev,
269
+ exposeDependencies: isDev,
270
+ resolveImportExtension: true,
271
+ reserveHtmlComments: "development"
272
+ };
273
+ while (true) {
274
+ const dir = dirname(id);
275
+ if (dir === id) {
276
+ break;
277
+ }
278
+ const got = qingkuaiConfigurations.get(id = dir);
279
+ if (got) {
280
+ config = got;
281
+ break;
282
+ }
283
+ }
284
+ return config;
285
+ }
286
+ function getReserveHtmlComments(config) {
287
+ switch (config.reserveHtmlComments) {
288
+ case "all":
289
+ return true;
290
+ case "never":
291
+ return false;
292
+ case "production":
293
+ return !isDev;
294
+ default:
295
+ return isDev;
296
+ }
297
+ }
298
+ function createQingkuaiConfigurationWatcher(server) {
299
+ const watcher = server.watcher.add([".qingkuairc", "!**/node_modules/**"]);
300
+ watcher.on("unlink", (path) => qingkuaiConfigurations.delete(dirname(path)));
301
+ watcher.on("change", recordQingkuaiConfiguration);
302
+ watcher.on("add", recordQingkuaiConfiguration);
303
+ }
304
+ function loadAllQingkuaiConfigurations(workspaceDir) {
305
+ findFilesByName(workspaceDir, ".qingkuairc", /* @__PURE__ */ new Set(["node_modules"])).forEach((fileName) => {
306
+ recordQingkuaiConfiguration(fileName);
307
+ });
308
+ }
309
+ function recordQingkuaiConfiguration(fileName) {
310
+ try {
311
+ qingkuaiConfigurations.set(dirname(fileName), JSON.parse(readFileSync(fileName, "utf-8")));
312
+ } catch {
313
+ }
314
+ }
315
+ }
316
+ function parseStyleId(id) {
317
+ const m1 = /^virtual:\[(\d+)\]/.exec(id);
318
+ const m2 = /\.[a-z]+\?\d{13}$/.exec(id);
319
+ if (!m1 || !m2) {
320
+ return {
321
+ index: -1,
322
+ fileId: ""
323
+ };
324
+ }
325
+ return {
326
+ index: parseInt(m1[1]),
327
+ fileId: id.slice(m1.index + m1[0].length, m2.index)
328
+ };
329
+ }
330
+
331
+ export { qingkuaiPlugin as default };
332
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,2 @@
1
+ import type { Plugin } from "vite";
2
+ export default function qingkuaiPlugin(): Plugin;
@@ -0,0 +1,5 @@
1
+ import type { SourceMap } from "./types";
2
+ export declare function attachScopeForStyleSelectors(code: string, hash: string, map: SourceMap, sourceFile: string): Promise<{
3
+ code: string;
4
+ mappings: string;
5
+ }>;
@@ -0,0 +1,4 @@
1
+ import type { SourceMap } from "./types";
2
+ import type { SourceMapMappings } from "@jridgewell/sourcemap-codec";
3
+ export declare function offsetSourceMap(mappings: string, sourceIndex: number, preLine: number, preColumn: number): SourceMapMappings;
4
+ export declare function getSourceFile(sourcemap: SourceMap, line: number, column: number): string;
@@ -0,0 +1,18 @@
1
+ export interface SourceMap {
2
+ version: number;
3
+ file?: string;
4
+ names: string[];
5
+ mappings: string;
6
+ sources: string[];
7
+ sourceRoot?: string;
8
+ sourcesContent?: string[];
9
+ x_google_ignoreList?: number[];
10
+ }
11
+ export type QingkuaiConfiguration = Partial<{
12
+ exposeDestructions: boolean;
13
+ exposeDependencies: boolean;
14
+ insertTipComments: boolean;
15
+ resolveImportExtension: boolean;
16
+ convenientDerivedDeclaration: boolean;
17
+ reserveHtmlComments: "all" | "never" | "development" | "production";
18
+ }>;
@@ -0,0 +1 @@
1
+ export declare function findFilesByName(dir: string, targetFileName: string, ignoreList: Set<string>): string[];
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "vite-plugin-qingkuai",
3
3
  "author": "mlgq",
4
4
  "license": "MIT",
5
- "version": "1.0.1",
5
+ "version": "1.0.3",
6
6
  "type": "module",
7
7
  "keywords": [
8
8
  "vite",
@@ -18,8 +18,8 @@
18
18
  },
19
19
  "scripts": {
20
20
  "dev": "rollup -c -w",
21
- "build": "rimraf dist && rollup -c",
22
- "postpublish": "curl -X PUT 'https://registry-direct.npmmirror.com/-/package/vite-plugin-qingkuai/sync'"
21
+ "build": "rimraf dist && tsc && rollup -c && rimraf dist/temp-types",
22
+ "postpublish": "curl -X PUT 'https://registry-direct.npmmirror.com/-/package/vite-plugin-qingkuai/syncs'"
23
23
  },
24
24
  "devDependencies": {
25
25
  "@types/fs-extra": "^11.0.4",
@@ -28,6 +28,7 @@
28
28
  "qingkuai": "^1.0.24",
29
29
  "rimraf": "^6.0.1",
30
30
  "rollup": "^4.38.0",
31
+ "rollup-plugin-dts": "^6.2.1",
31
32
  "rollup-plugin-esbuild": "^6.2.1",
32
33
  "vite": "^6.2.4"
33
34
  },
package/.prettierrc DELETED
@@ -1,7 +0,0 @@
1
- {
2
- "tabWidth": 4,
3
- "semi": false,
4
- "printWidth": 120,
5
- "arrowParens": "avoid",
6
- "trailingComma": "none"
7
- }
package/rollup.config.js DELETED
@@ -1,44 +0,0 @@
1
- import * as rollup from "rollup"
2
- import esbuild from "rollup-plugin-esbuild"
3
-
4
- export default rollup.defineConfig(() => {
5
- const inputOptions = {
6
- external: [
7
- "vite",
8
- "postcss",
9
- "node:fs",
10
- "node:path",
11
- "node:crypto",
12
- "source-map-js",
13
- "merge-source-map",
14
- "qingkuai/compiler",
15
- "@ampproject/remapping",
16
- "postcss-selector-parser",
17
- "@jridgewell/sourcemap-codec"
18
- ],
19
- input: {
20
- index: "./src/index.ts"
21
- },
22
- output: {
23
- dir: "dist",
24
- format: "es",
25
- sourcemap: true
26
- },
27
- plugins: [
28
- esbuild({
29
- target: "esNext"
30
- })
31
- ]
32
- }
33
-
34
- return [
35
- inputOptions,
36
- Object.assign({}, inputOptions, {
37
- output: {
38
- dir: "dist",
39
- format: "cjs",
40
- entryFileNames: "[name].cjs"
41
- }
42
- })
43
- ]
44
- })
@@ -1,24 +0,0 @@
1
- import type { SourceMap } from "./types"
2
-
3
- import fs from "fs-extra"
4
- import path from "node:path"
5
-
6
- export function generateSourceMapFiles(generate: string, map: SourceMap) {
7
- const sourcemapPath = path.resolve(__dirname, "../sourcemap-test")
8
- const gpath = path.resolve(sourcemapPath, "./g.css")
9
- const mpath = path.resolve(sourcemapPath, "./s.css.map")
10
- fs.removeSync(gpath)
11
- fs.removeSync(mpath)
12
- fs.writeFileSync(gpath, generate, "utf-8")
13
- fs.writeFileSync(
14
- mpath,
15
- JSON.stringify({
16
- version: 3,
17
- names: map.names,
18
- sources: map.sources,
19
- mappings: map.mappings,
20
- sourcesContent: map.sourcesContent
21
- }),
22
- "utf-8"
23
- )
24
- }
package/src/index.ts DELETED
@@ -1,280 +0,0 @@
1
- import type { CompileResult } from "qingkuai/compiler"
2
- import type { QingkuaiConfiguration, SourceMap } from "./types"
3
- import type { Plugin, ResolvedConfig, ViteDevServer } from "vite"
4
-
5
- import { findFilesByName } from "./util"
6
- import { randomBytes } from "node:crypto"
7
- import { offsetSourceMap } from "./sourcemap"
8
- import { compile, util } from "qingkuai/compiler"
9
- import { existsSync, readFileSync } from "node:fs"
10
- import { encode } from "@jridgewell/sourcemap-codec"
11
- import { basename, dirname, extname } from "node:path"
12
- import { attachScopeForStyleSelectors } from "./scope"
13
- import { transformWithEsbuild, preprocessCSS } from "vite"
14
-
15
- export default function qingkuaiPlugin(): Plugin {
16
- let isDev: boolean
17
- let sourcemap: boolean
18
- let cssSourcemap: boolean
19
- let viteConfig: ResolvedConfig
20
-
21
- const parseFailedConfigFiles: string[] = []
22
- const compileResultCache = new Map<string, CompileResult>()
23
- const qingkuaiConfigurations = new Map<string, QingkuaiConfiguration>()
24
-
25
- const confIdentifierRE = /__qk_expose_(?:dependencies|destructions)__/g
26
- const styleIdRE = /^virtual:\[\d+\].*?\.qk.(?:css|s[ac]ss|less|stylus|postcss)\?\d{13}$/
27
- const qingkuaiPackageRE = /node_modules\/qingkuai\/dist\/esm\/(?:chunks|runtime)\/\w+\.js$/
28
-
29
- return {
30
- name: "qingkuai-compiler",
31
-
32
- config(_, env) {
33
- isDev = env.command === "serve"
34
- },
35
-
36
- configureServer(server) {
37
- createQingkuaiConfigurationWatcher(server)
38
- },
39
-
40
- configResolved(config) {
41
- viteConfig = config
42
- if (isDev) {
43
- sourcemap = true
44
- cssSourcemap = !!config.css.devSourcemap
45
- } else {
46
- cssSourcemap = true
47
- sourcemap = config.build.sourcemap !== false
48
- }
49
- loadAllQingkuaiConfigurations(config.root)
50
- },
51
-
52
- resolveId(id, importer) {
53
- if (styleIdRE.test(id)) {
54
- return id
55
- }
56
- if (importer?.endsWith(".qk") && !extname(id)) {
57
- const qingkuaiConfig = getQingkuaiConfiguration(id)
58
- return id + (qingkuaiConfig.resolveImportExtension ? ".qk" : "")
59
- }
60
- },
61
-
62
- async load(id) {
63
- if (styleIdRE.test(id)) {
64
- const { fileId, index } = parseStyleId(id)
65
- if (index === -1) {
66
- return ""
67
- }
68
-
69
- let virtualFileName: string
70
- const compileRes = compileResultCache.get(fileId)!
71
- const style = compileRes.inputDescriptor.styles[index]
72
-
73
- // create a relative and not existing file name
74
- while (true) {
75
- const hash = randomBytes(6).toString("hex")
76
- virtualFileName = `${fileId}.${hash}.${style.lang}`
77
- if (!existsSync(virtualFileName)) {
78
- break
79
- }
80
- }
81
-
82
- const preprocessRes = await preprocessCSS(style.code, virtualFileName, {
83
- ...viteConfig,
84
- css: {
85
- ...viteConfig.css,
86
- postcss: {
87
- from: virtualFileName
88
- }
89
- }
90
- })
91
- if (!cssSourcemap) {
92
- return preprocessRes.code
93
- }
94
-
95
- const assertedPreprocessMap = preprocessRes.map as SourceMap
96
- const attachScopeResult = await attachScopeForStyleSelectors(
97
- preprocessRes.code,
98
- compileRes.hashId,
99
- assertedPreprocessMap,
100
- virtualFileName
101
- )
102
- const offsetMappings = encode(
103
- offsetSourceMap(
104
- attachScopeResult.mappings,
105
- preprocessRes.deps?.size || 0,
106
- style.loc.start.line - 1,
107
- style.loc.start.column - 1
108
- )
109
- )
110
- return {
111
- code: attachScopeResult.code,
112
- map: {
113
- version: 3,
114
- mappings: offsetMappings,
115
- names: assertedPreprocessMap.names,
116
- sources: [...assertedPreprocessMap.sources.slice(0, -1), fileId]
117
- }
118
- }
119
- }
120
- },
121
-
122
- async transform(src, id) {
123
- const qingkuaiConfig = getQingkuaiConfiguration(id)
124
- if (!id.endsWith(".qk")) {
125
- if (!isDev && qingkuaiPackageRE.test(id)) {
126
- const ret = src.replace(confIdentifierRE, s => {
127
- switch (s) {
128
- case "__qk_expose_dependencies__":
129
- return JSON.stringify(!!qingkuaiConfig.exposeDependencies)
130
- case "__qk_expose_destructions__":
131
- return JSON.stringify(!!qingkuaiConfig.exposeDestructions)
132
- default:
133
- return s
134
- }
135
- })
136
- return ret
137
- }
138
- return
139
- }
140
-
141
- const compileRes = compile(src, {
142
- sourcemap,
143
- debug: isDev,
144
- hashId: compileResultCache.get(id)?.hashId || undefined,
145
- reserveTemplateComment: getReserveHtmlComments(qingkuaiConfig),
146
- componentName: util.kebab2Camel(basename(id, extname(id)), true)
147
- })
148
- compileRes.messages.forEach(({ type, value: warning }) => {
149
- if (type === "warning") {
150
- this.warn(warning.message)
151
- }
152
- })
153
- compileResultCache.set(id, compileRes)
154
-
155
- const compiledCodeArr = [compileRes.code]
156
- compileRes.inputDescriptor.styles.forEach((_, index) => {
157
- compiledCodeArr.push(`import "virtual:[${index}]${id}.css?${Date.now()}"`)
158
- })
159
-
160
- const baseMap: any = {
161
- version: 3,
162
- sources: [id],
163
- sourcesContent: [src]
164
- }
165
- const compiledCode = compiledCodeArr.join("\n")
166
- if (!compileRes.inputDescriptor.script.isTS) {
167
- if (!sourcemap) {
168
- return compiledCode
169
- }
170
- return {
171
- code: compiledCode,
172
- map: Object.assign(baseMap, {
173
- mappings: compileRes.mappings
174
- })
175
- }
176
- }
177
-
178
- const esBuildCompileRes = await transformWithEsbuild(
179
- compiledCode,
180
- id,
181
- {
182
- sourcemap,
183
- loader: "ts",
184
- target: "esnext"
185
- },
186
- sourcemap
187
- ? {
188
- version: 3,
189
- sources: [id],
190
- sourcesContent: [src],
191
- mappings: compileRes.mappings
192
- }
193
- : undefined
194
- )
195
-
196
- if (!sourcemap) {
197
- return esBuildCompileRes.code
198
- }
199
- return {
200
- code: esBuildCompileRes.code,
201
- map: Object.assign(baseMap, {
202
- mappings: esBuildCompileRes.map.mappings
203
- })
204
- }
205
- }
206
- }
207
-
208
- function getQingkuaiConfiguration(id: string) {
209
- let config: QingkuaiConfiguration = {
210
- insertTipComments: true,
211
- exposeDestructions: isDev,
212
- exposeDependencies: isDev,
213
- resolveImportExtension: true,
214
- reserveHtmlComments: "development"
215
- }
216
- while (true) {
217
- const dir = dirname(id)
218
- if (dir === id) {
219
- break
220
- }
221
-
222
- const got = qingkuaiConfigurations.get((id = dir))
223
- if (got) {
224
- config = got
225
- break
226
- }
227
- }
228
- return config
229
- }
230
-
231
- function getReserveHtmlComments(config: QingkuaiConfiguration) {
232
- switch (config.reserveHtmlComments) {
233
- case "all":
234
- return true
235
- case "never":
236
- return false
237
- case "production":
238
- return !isDev
239
- default:
240
- return isDev
241
- }
242
- }
243
-
244
- function createQingkuaiConfigurationWatcher(server: ViteDevServer) {
245
- const watcher = server.watcher.add([".qingkuairc", "!**/node_modules/**"])
246
- watcher.on("unlink", path => qingkuaiConfigurations.delete(dirname(path)))
247
- watcher.on("change", recordQingkuaiConfiguration)
248
- watcher.on("add", recordQingkuaiConfiguration)
249
- }
250
-
251
- function loadAllQingkuaiConfigurations(workspaceDir: string) {
252
- findFilesByName(workspaceDir, ".qingkuairc", new Set(["node_modules"])).forEach(fileName => {
253
- recordQingkuaiConfiguration(fileName)
254
- })
255
- }
256
-
257
- function recordQingkuaiConfiguration(fileName: string) {
258
- try {
259
- qingkuaiConfigurations.set(dirname(fileName), JSON.parse(readFileSync(fileName, "utf-8")))
260
- } catch {
261
- parseFailedConfigFiles.push(fileName)
262
- }
263
- }
264
- }
265
-
266
- function parseStyleId(id: string) {
267
- const m1 = /^virtual:\[(\d+)\]/.exec(id)!
268
- const m2 = /\.[a-z]+\?\d{13}$/.exec(id)!
269
- if (!m1 || !m2) {
270
- return {
271
- index: -1,
272
- fileId: ""
273
- }
274
- }
275
-
276
- return {
277
- index: parseInt(m1[1]),
278
- fileId: id.slice(m1.index + m1[0].length, m2.index)
279
- }
280
- }
package/src/scope.ts DELETED
@@ -1,47 +0,0 @@
1
- import type { SourceMap } from "./types"
2
-
3
- import postcss from "postcss"
4
- import { getSourceFile } from "./sourcemap"
5
- import selectorParser from "postcss-selector-parser"
6
-
7
- export async function attachScopeForStyleSelectors(code: string, hash: string, map: SourceMap, sourceFile: string) {
8
- const processor = postcss([
9
- {
10
- postcssPlugin: "postcss-attach-scope-qingkuai",
11
- Rule(rule) {
12
- const { line, column } = rule.source!.start!
13
- if (getSourceFile(map, line, column) !== sourceFile) {
14
- return
15
- }
16
-
17
- rule.selector = selectorParser(selectors => {
18
- selectors.each(selector => {
19
- const index = selector.nodes.findLastIndex(({ type }) => {
20
- return type === "class" || type === "tag" || type === "id" || type === "universal"
21
- })
22
- if (index !== -1) {
23
- const lastNode = selector.nodes[index]!
24
- lastNode.parent?.insertAfter(
25
- lastNode,
26
- selectorParser.attribute({
27
- attribute: `qk-${hash}`,
28
- value: undefined,
29
- raws: {}
30
- })
31
- )
32
- }
33
- })
34
- }).processSync(rule.selector)
35
- }
36
- }
37
- ])
38
-
39
- const ret = await processor.process(code, {
40
- from: map.file,
41
- map: {
42
- prev: map,
43
- annotation: false
44
- }
45
- })
46
- return { code: ret.css, mappings: ret.map.toJSON().mappings }
47
- }
package/src/sourcemap.ts DELETED
@@ -1,38 +0,0 @@
1
- import type { SourceMap } from "./types"
2
- import type { SourceMapMappings } from "@jridgewell/sourcemap-codec"
3
-
4
- import { SourceMapConsumer } from "source-map-js"
5
- import { decode } from "@jridgewell/sourcemap-codec"
6
-
7
- export function offsetSourceMap(
8
- mappings: string,
9
- sourceIndex: number,
10
- preLine: number,
11
- preColumn: number
12
- ): SourceMapMappings {
13
- return decode(mappings).map(line => {
14
- return line.map(segment => {
15
- if (segment.length === 1 || segment[1] !== sourceIndex) {
16
- return segment
17
- }
18
- if (segment[2] === preLine) {
19
- segment[3] += preColumn
20
- } else {
21
- segment[2] += preLine
22
- }
23
- return segment
24
- })
25
- })
26
- }
27
-
28
- export function getSourceFile(sourcemap: SourceMap, line: number, column: number) {
29
- const consumer = new SourceMapConsumer({
30
- ...sourcemap,
31
- version: sourcemap.version.toString()
32
- })
33
-
34
- return consumer.originalPositionFor({
35
- line: line,
36
- column: column
37
- }).source
38
- }
package/src/types.ts DELETED
@@ -1,19 +0,0 @@
1
- export interface SourceMap {
2
- version: number
3
- file?: string
4
- names: string[]
5
- mappings: string
6
- sources: string[]
7
- sourceRoot?: string
8
- sourcesContent?: string[]
9
- x_google_ignoreList?: number[]
10
- }
11
-
12
- export type QingkuaiConfiguration = Partial<{
13
- exposeDestructions: boolean
14
- exposeDependencies: boolean
15
- insertTipComments: boolean
16
- resolveImportExtension: boolean
17
- convenientDerivedDeclaration: boolean
18
- reserveHtmlComments: "all" | "never" | "development" | "production"
19
- }>
package/src/util.ts DELETED
@@ -1,21 +0,0 @@
1
- import { join } from "node:path"
2
- import { readdirSync } from "node:fs"
3
-
4
- export function findFilesByName(dir: string, targetFileName: string, ignoreList: Set<string>) {
5
- const result: string[] = []
6
- const entries = readdirSync(dir, { withFileTypes: true })
7
-
8
- for (const entry of entries) {
9
- if (ignoreList.has(entry.name)) {
10
- continue
11
- }
12
-
13
- const fullPath = join(dir, entry.name)
14
- if (entry.name === targetFileName) {
15
- result.push(fullPath)
16
- } else if (entry.isDirectory()) {
17
- result.push(...findFilesByName(fullPath, targetFileName, ignoreList))
18
- }
19
- }
20
- return result
21
- }
package/tsconfig.json DELETED
@@ -1,9 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "strict": true,
4
- "noEmit": true,
5
- "target": "ESNext",
6
- "skipLibCheck": true,
7
- "moduleResolution": "bundler"
8
- }
9
- }