weapp-vite 0.0.2-alpha.3 → 1.0.0

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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2021 ice breaker
3
+ Copyright (c) 2024 ice breaker
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md ADDED
@@ -0,0 +1,77 @@
1
+ # weapp-vite
2
+
3
+ > 给小程序现代化的开发体验
4
+
5
+ - [x] Vite 支持,带来了 `typescript` / `scss` / `less` 等等所有支持
6
+ - [x] 插件支持,可以使用 `weapp-tailwindcss` 等等插件,也可以自定义编写插件,方便扩展
7
+
8
+ ## 使用方式
9
+
10
+ 在你的小程序目录下,使用 `npm init -y` 创建一个 `package.json`
11
+
12
+ 然后执行:
13
+
14
+ ```sh
15
+ npm i -D weapp-vite
16
+ # 执行初始化命令
17
+ npx weapp-vite init
18
+ ```
19
+
20
+ ### 热更新开发命令
21
+
22
+ ```sh
23
+ npm run dev
24
+ ```
25
+
26
+ ### 构建命令
27
+
28
+ ```sh
29
+ npm run build
30
+ ```
31
+
32
+ ### 构建npm命令
33
+
34
+ ```sh
35
+ npm run build-npm
36
+ ```
37
+
38
+ ### 打开微信开发者工具命令
39
+
40
+ ```sh
41
+ npm run open
42
+ ```
43
+
44
+ ## 配置项
45
+
46
+ 配置项可以与 `vite` 通用,同时加入了 `weapp-vite` 的扩展:
47
+
48
+ `vite.config.ts`:
49
+
50
+ ```ts
51
+ import { defineConfig } from 'weapp-vite/config'
52
+
53
+ export default defineConfig({
54
+ // 其他的配置同
55
+ weapp: {
56
+ // 用来配置监听 app.json 所在的目录
57
+ // 比如默认情况下 ts 创建的项目,app.json 所在为 './miniprogram'
58
+ srcRoot: './miniprogram',
59
+ // weapp-vite options
60
+ },
61
+ })
62
+ ```
63
+
64
+ ## Contribute
65
+
66
+ 我们邀请你来贡献和帮助改进 `weapp-vite` 💚💚💚
67
+
68
+ 以下有几个方式可以参与:
69
+
70
+ - 报告错误:如果您遇到任何错误或问题,请提`issue`并提供完善的错误信息和复现方式。
71
+ - 建议:有增强 `weapp-vite` 的想法吗?请提 `issue` 来分享您的建议。
72
+ - 文档:如果您对文档有更好的见解或者更棒的修辞方式,欢迎 `pr`。
73
+ - 代码:任何人的代码都不是完美的,我们欢迎你通过 `pr` 给代码提供更好的质量与活力。
74
+
75
+ ## License
76
+
77
+ [MIT](./LICENSE)
@@ -1,23 +1,27 @@
1
1
  // src/build.ts
2
2
  import process2 from "node:process";
3
3
  import { build } from "vite";
4
- import { addExtension as addExtension3, defu as defu2, removeExtension as removeExtension3 } from "@weapp-core/shared";
4
+ import { addExtension as addExtension3, defu as defu3, removeExtension as removeExtension3 } from "@weapp-core/shared";
5
+ import { readPackageJSON } from "pkg-types";
6
+ import path5 from "pathe";
5
7
 
6
8
  // src/plugins/index.ts
7
9
  import path4 from "node:path";
8
10
  import fs3 from "fs-extra";
9
11
  import MagicString from "magic-string";
10
12
  import { addExtension as addExtension2, removeExtension as removeExtension2 } from "@weapp-core/shared";
11
- import fg from "fast-glob";
12
13
  import { isCSSRequest, preprocessCSS } from "vite";
14
+ import fg from "fast-glob";
13
15
 
14
16
  // src/utils/scan.ts
15
17
  import path from "pathe";
16
18
  import fs from "fs-extra";
17
- import klaw from "klaw";
18
- import { addExtension, isObject, removeExtension } from "@weapp-core/shared";
19
+ import { addExtension, defu, isObject, removeExtension } from "@weapp-core/shared";
19
20
  var defaultExcluded = ["**/node_modules/**", "**/miniprogram_npm/**"];
20
- function searchAppEntry(root, formatPath) {
21
+ function searchAppEntry(options) {
22
+ const { formatPath, root } = defu(options, {
23
+ formatPath: (x) => x
24
+ });
21
25
  const extensions = ["js", "ts"];
22
26
  const appJsonPath = path.resolve(root, "app.json");
23
27
  if (fs.existsSync(appJsonPath)) {
@@ -38,19 +42,19 @@ function searchAppEntry(root, formatPath) {
38
42
  if (isObject(appJson.usingComponents)) {
39
43
  deps.push(...parseJsonUseComponents(appJson));
40
44
  }
41
- if (Array.isArray(appJson.subpackages)) {
42
- for (const subpackage of appJson.subpackages) {
43
- deps.push(...subpackage.map((x) => {
44
- return {
45
- type: "subpackage",
46
- ...x
47
- };
48
- }));
49
- }
45
+ if (Array.isArray(appJson.subPackages)) {
46
+ deps.push(...appJson.subPackages.map((x) => {
47
+ return {
48
+ type: "subPackage",
49
+ ...x
50
+ };
51
+ }));
50
52
  }
51
53
  }
52
54
  return {
53
- path: formatPath ? formatPath(entryPath) : entryPath,
55
+ jsonPath: formatPath(appJsonPath),
56
+ json: appJson,
57
+ path: formatPath(entryPath),
54
58
  deps,
55
59
  type: "app"
56
60
  };
@@ -93,13 +97,17 @@ function getWxmlEntry(wxmlPath, formatPath) {
93
97
  return {
94
98
  deps: parseJsonUseComponents(json),
95
99
  path: finalPath,
96
- type: "component"
100
+ type: "component",
101
+ json,
102
+ jsonPath: formatPath(jsonPath)
97
103
  };
98
104
  } else {
99
105
  return {
100
106
  deps: parseJsonUseComponents(json),
101
107
  path: finalPath,
102
- type: "page"
108
+ type: "page",
109
+ json,
110
+ jsonPath: formatPath(jsonPath)
103
111
  };
104
112
  }
105
113
  }
@@ -107,45 +115,8 @@ function getWxmlEntry(wxmlPath, formatPath) {
107
115
  deps: [],
108
116
  path: finalPath,
109
117
  type: "page"
110
- };
111
- }
112
- }
113
- async function scanEntries(root, options) {
114
- function formatPath(to) {
115
- if (options?.relative) {
116
- return path.relative(root, to);
117
- }
118
- return path.normalize(to);
119
- }
120
- const appEntry = searchAppEntry(root, formatPath);
121
- if (appEntry) {
122
- const pageEntries = [];
123
- const componentEntries = [];
124
- for await (const file of klaw(
125
- root,
126
- {
127
- filter: options?.filter
128
- }
129
- )) {
130
- if (file.stats.isFile()) {
131
- if (/\.wxml$/.test(file.path)) {
132
- const entry = getWxmlEntry(file.path, formatPath);
133
- if (entry) {
134
- if (entry.type === "component") {
135
- componentEntries.push(entry);
136
- } else if (entry.type === "page") {
137
- pageEntries.push(entry);
138
- }
139
- }
140
- }
141
- }
142
- }
143
- return {
144
- app: appEntry,
145
- pages: pageEntries,
146
- components: componentEntries,
147
- subpackages: []
148
- // walkPathsSet,
118
+ // json: undefined,
119
+ // jsonPath: undefined,
149
120
  };
150
121
  }
151
122
  }
@@ -161,11 +132,13 @@ var supportedCssExtensions = supportedCssLangs.map((x) => `.${x}`);
161
132
  // src/entry.ts
162
133
  import process from "node:process";
163
134
  import mm from "micromatch";
164
- import { defu } from "@weapp-core/shared";
135
+ import { defu as defu2 } from "@weapp-core/shared";
165
136
  import path3 from "pathe";
137
+ import klaw from "klaw";
166
138
  function createFilter(include, exclude, options) {
167
- const opts = defu(options, {
139
+ const opts = defu2(options, {
168
140
  ignore: exclude
141
+ // debug: true,
169
142
  // dot: true,
170
143
  // contains: true,
171
144
  });
@@ -179,42 +152,105 @@ function createFilter(include, exclude, options) {
179
152
  return mm.isMatch(id, include, opts);
180
153
  };
181
154
  }
182
- function getEntries(options) {
183
- const { root = process.cwd(), outDir = "dist", relative, srcRoot = "" } = options;
184
- const filter = createFilter(
185
- [path3.join(srcRoot, "**/*")],
186
- [...defaultExcluded, path3.join(root, `${outDir}/**`)],
187
- { cwd: root }
188
- );
189
- return scanEntries(root, { filter, relative });
190
- }
191
-
192
- // src/cache.ts
193
- function createPluginCache(cache) {
194
- return {
195
- delete(id) {
196
- return delete cache[id];
197
- },
198
- get(id) {
199
- const item = cache[id];
200
- if (!item) {
201
- return;
155
+ async function getEntries(options) {
156
+ const { root = process.cwd(), outDir = "dist", relative, srcRoot = "", subPackage } = options;
157
+ function formatPath(to) {
158
+ if (relative) {
159
+ return path3.relative(root, to);
160
+ }
161
+ return path3.normalize(to);
162
+ }
163
+ if (subPackage) {
164
+ const subPackageRoot = subPackage.root ?? "";
165
+ const filter = createFilter(
166
+ [path3.join(root, srcRoot, subPackageRoot, "**/*")],
167
+ [
168
+ ...defaultExcluded
169
+ ],
170
+ { cwd: root }
171
+ );
172
+ const pageEntries = [];
173
+ const componentEntries = [];
174
+ const subPackageEntries = [];
175
+ if (subPackage.entry) {
176
+ subPackageEntries.push({
177
+ deps: [],
178
+ path: path3.join(root, subPackageRoot, subPackage.entry),
179
+ type: "subPackageEntry"
180
+ });
181
+ }
182
+ for await (const file of klaw(
183
+ path3.join(root, subPackageRoot),
184
+ {
185
+ filter
202
186
  }
203
- item[0] = 0;
204
- return item[1];
205
- },
206
- has(id) {
207
- const item = cache[id];
208
- if (!item) {
209
- return false;
187
+ )) {
188
+ if (file.stats.isFile()) {
189
+ if (/\.wxml$/.test(file.path)) {
190
+ const entry = getWxmlEntry(file.path, formatPath);
191
+ if (entry) {
192
+ if (entry.type === "component") {
193
+ componentEntries.push(entry);
194
+ } else if (entry.type === "page") {
195
+ pageEntries.push(entry);
196
+ }
197
+ }
198
+ }
210
199
  }
211
- item[0] = 0;
212
- return true;
213
- },
214
- set(id, value) {
215
- cache[id] = [0, value];
216
200
  }
217
- };
201
+ return {
202
+ pages: pageEntries,
203
+ components: componentEntries,
204
+ subPackageEntries
205
+ };
206
+ } else {
207
+ const appEntry = searchAppEntry({
208
+ root: path3.join(root, srcRoot),
209
+ formatPath
210
+ });
211
+ if (appEntry) {
212
+ const subPackageDeps = appEntry.deps.filter((x) => x.type === "subPackage");
213
+ const filter = createFilter(
214
+ [path3.join(root, srcRoot, "**/*")],
215
+ [
216
+ ...defaultExcluded,
217
+ path3.join(root, `${outDir}/**`),
218
+ ...subPackageDeps.map((x) => {
219
+ return path3.join(root, `${x.root}/**`);
220
+ })
221
+ ],
222
+ { cwd: root }
223
+ );
224
+ const pageEntries = [];
225
+ const componentEntries = [];
226
+ for await (const file of klaw(
227
+ path3.join(root, srcRoot),
228
+ {
229
+ filter
230
+ }
231
+ )) {
232
+ if (file.stats.isFile()) {
233
+ if (/\.wxml$/.test(file.path)) {
234
+ const entry = getWxmlEntry(file.path, formatPath);
235
+ if (entry) {
236
+ if (entry.type === "component") {
237
+ componentEntries.push(entry);
238
+ } else if (entry.type === "page") {
239
+ pageEntries.push(entry);
240
+ }
241
+ }
242
+ }
243
+ }
244
+ }
245
+ return {
246
+ app: appEntry,
247
+ pages: pageEntries,
248
+ components: componentEntries,
249
+ subPackages: subPackageDeps
250
+ // walkPathsSet,
251
+ };
252
+ }
253
+ }
218
254
  }
219
255
 
220
256
  // src/debugger.ts
@@ -250,7 +286,7 @@ function getRealPath(res) {
250
286
  }
251
287
  return res.filename;
252
288
  }
253
- function vitePluginWeapp() {
289
+ function vitePluginWeapp(ctx) {
254
290
  function getInputOption(entries) {
255
291
  return entries.reduce((acc, cur) => {
256
292
  acc[relative(cur)] = cur;
@@ -263,33 +299,112 @@ function vitePluginWeapp() {
263
299
  function relative(p) {
264
300
  return path4.relative(configResolved.root, p);
265
301
  }
266
- const cacheInstance = createPluginCache(/* @__PURE__ */ Object.create(null));
302
+ let appEntry;
267
303
  return [
268
304
  {
269
305
  name: "weapp-vite:pre",
270
306
  enforce: "pre",
307
+ api: {},
271
308
  // config->configResolved->|watching|options->buildStart
272
309
  config(config, env) {
273
310
  debug?.(config, env);
311
+ if (config?.weapp?.srcRoot && !config?.weapp.subPackage && !ctx.srcRootRef.value) {
312
+ ctx.srcRootRef.value = config.weapp.srcRoot;
313
+ }
314
+ },
315
+ configResolved(config) {
316
+ debug?.(config);
317
+ configResolved = config;
274
318
  },
275
319
  async options(options) {
320
+ const { root, build: build2, weapp } = configResolved;
276
321
  const entries = await getEntries({
277
- root: configResolved.root,
278
- outDir: configResolved.build.outDir,
279
- srcRoot: configResolved.weapp?.srcRoot
322
+ root,
323
+ outDir: build2.outDir,
324
+ srcRoot: weapp?.srcRoot,
325
+ subPackage: weapp?.subPackage
280
326
  });
281
327
  if (entries) {
282
- const paths = [entries.app, ...entries.pages, ...entries.components].map((x) => {
328
+ const paths = [];
329
+ if (entries.app) {
330
+ paths.push(entries.app.path);
331
+ appEntry = entries.app;
332
+ }
333
+ paths.push(...[...entries.pages, ...entries.components].map((x) => {
283
334
  return x.path;
284
- });
335
+ }));
336
+ if (entries.subPackageEntries) {
337
+ paths.push(...entries.subPackageEntries.map((x) => {
338
+ return x.path;
339
+ }));
340
+ }
285
341
  const input = getInputOption(paths);
286
342
  entriesSet = new Set(paths);
287
343
  options.input = input;
344
+ if (Array.isArray(entries.subPackages) && entries.subPackages.length) {
345
+ for (const subPackage of entries.subPackages) {
346
+ if (subPackage.root && !ctx.watcherCache.has(subPackage.root)) {
347
+ if (ctx.isDev) {
348
+ runDev(ctx, {
349
+ weapp: {
350
+ subPackage
351
+ }
352
+ });
353
+ } else {
354
+ runProd(ctx, {
355
+ weapp: {
356
+ subPackage
357
+ }
358
+ });
359
+ }
360
+ }
361
+ }
362
+ }
288
363
  }
289
364
  },
290
- configResolved(config) {
291
- debug?.(config);
292
- configResolved = config;
365
+ async buildStart() {
366
+ const { root, build: build2, weapp } = configResolved;
367
+ let cwd = root;
368
+ const ignore = [
369
+ ...defaultExcluded
370
+ ];
371
+ if (!appEntry && weapp?.subPackage && weapp.subPackage.root) {
372
+ cwd = path4.join(root, weapp.subPackage.root);
373
+ } else {
374
+ ignore.push(
375
+ ...[
376
+ `${build2.outDir}/**`,
377
+ ...appEntry.deps.filter(
378
+ (x) => x.type === "subPackage"
379
+ ).map((x) => {
380
+ return `${x.root}/**`;
381
+ }),
382
+ "project.config.json",
383
+ "project.private.config.json",
384
+ "package.json",
385
+ "tsconfig.json",
386
+ "tsconfig.node.json"
387
+ ]
388
+ );
389
+ }
390
+ const files = await fg(
391
+ // 假如去 join root 就是返回 absolute
392
+ [path4.join(weapp?.srcRoot ?? "", "**/*.{wxml,json,wxs,png,jpg,jpeg,gif,svg,webp}")],
393
+ {
394
+ cwd,
395
+ ignore,
396
+ absolute: false
397
+ }
398
+ );
399
+ for (const file of files) {
400
+ const filepath = path4.resolve(cwd, file);
401
+ this.addWatchFile(filepath);
402
+ this.emitFile({
403
+ type: "asset",
404
+ fileName: ctx.relativeSrcRoot(file),
405
+ source: await fs3.readFile(filepath, "utf8")
406
+ });
407
+ }
293
408
  },
294
409
  resolveId(source) {
295
410
  if (/\.wxss$/.test(source)) {
@@ -335,34 +450,10 @@ function vitePluginWeapp() {
335
450
  for (const style of Object.entries(styles)) {
336
451
  this.emitFile({
337
452
  type: "asset",
338
- fileName: style[0],
453
+ fileName: ctx.relativeSrcRoot(style[0]),
339
454
  source: style[1]
340
455
  });
341
456
  }
342
- const files = await fg(
343
- // 假如去 join root 就是返回 absolute
344
- ["**/*.{wxml,json,wxs,png,jpg,jpeg,gif,svg,webp}"],
345
- {
346
- cwd: configResolved.root,
347
- ignore: [
348
- ...defaultExcluded,
349
- `${configResolved.build.outDir}/**`,
350
- "project.config.json",
351
- "project.private.config.json",
352
- "package.json"
353
- ],
354
- absolute: false
355
- }
356
- );
357
- for (const file of files) {
358
- const filepath = path4.resolve(configResolved.root, file);
359
- this.addWatchFile(filepath);
360
- this.emitFile({
361
- type: "asset",
362
- fileName: file,
363
- source: await fs3.readFile(filepath, "utf8")
364
- });
365
- }
366
457
  }
367
458
  // generateBundle(_options, _bundle) {
368
459
  // const files = this.getWatchFiles()
@@ -379,38 +470,55 @@ function vitePluginWeapp() {
379
470
  ];
380
471
  }
381
472
 
473
+ // src/symbols.ts
474
+ var RootSymbol = Symbol("root");
475
+ var MiscSymbol = Symbol("misc");
476
+
382
477
  // src/build.ts
383
- function getDefaultConfig() {
478
+ async function getDefaultConfig(ctx) {
479
+ const localPackageJson = await readPackageJSON();
480
+ const external = [];
481
+ if (localPackageJson.dependencies) {
482
+ external.push(...Object.keys(localPackageJson.dependencies));
483
+ }
384
484
  return {
385
485
  build: {
386
486
  rollupOptions: {
387
487
  output: {
388
488
  format: "cjs",
389
489
  entryFileNames: (chunkInfo) => {
390
- if (chunkInfo.name.endsWith(".ts")) {
391
- return addExtension3(removeExtension3(chunkInfo.name), ".js");
490
+ const name = ctx.relativeSrcRoot(chunkInfo.name);
491
+ if (name.endsWith(".ts")) {
492
+ const baseFileName = removeExtension3(name);
493
+ if (baseFileName.endsWith(".wxs")) {
494
+ return path5.normalize(baseFileName);
495
+ }
496
+ return path5.normalize(addExtension3(baseFileName, ".js"));
392
497
  }
393
- return chunkInfo.name;
498
+ return path5.normalize(name);
394
499
  }
395
- }
500
+ },
501
+ external
396
502
  },
397
503
  assetsDir: ".",
398
504
  commonjsOptions: {
399
505
  transformMixedEsModules: true,
400
506
  include: void 0
401
- }
507
+ },
508
+ emptyOutDir: false
402
509
  },
403
510
  plugins: [
404
- vitePluginWeapp()
511
+ vitePluginWeapp(ctx)
405
512
  ]
513
+ // logLevel: 'silent',
406
514
  };
407
515
  }
408
- async function runDev(options) {
516
+ async function runDev(ctx, options) {
409
517
  process2.env.NODE_ENV = "development";
410
518
  const watcher = await build(
411
- defu2(
519
+ defu3(
412
520
  options,
413
- getDefaultConfig(),
521
+ await getDefaultConfig(ctx),
414
522
  {
415
523
  mode: "development",
416
524
  build: {
@@ -420,13 +528,19 @@ async function runDev(options) {
420
528
  }
421
529
  )
422
530
  );
531
+ ctx.watcherCache.set(options?.weapp?.subPackage?.root || RootSymbol, watcher);
423
532
  return watcher;
424
533
  }
425
- async function runProd(options) {
534
+ async function runProd(ctx, options) {
426
535
  const output = await build(
427
- defu2(
536
+ defu3(
428
537
  options,
429
- getDefaultConfig(),
538
+ // {
539
+ // build: {
540
+ // emptyOutDir: true,
541
+ // },
542
+ // },
543
+ await getDefaultConfig(ctx),
430
544
  {
431
545
  mode: "production"
432
546
  }