@into-mini/sfc-split-plugin 0.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 +21 -0
- package/helper/empty.json +1 -0
- package/helper/hooks.mjs +48 -0
- package/helper/index.mjs +62 -0
- package/helper/read.mjs +37 -0
- package/helper/utils.mjs +6 -0
- package/loader/fake-vue-loader.mjs +140 -0
- package/loader/hack-entry-loader.mjs +5 -0
- package/package.json +51 -0
- package/plugin/add-entry.mjs +85 -0
- package/plugin/add-wxs.mjs +62 -0
- package/plugin/copy-config.mjs +89 -0
- package/plugin/emit-fake.mjs +35 -0
- package/plugin/expose-entry-bk.mjs +73 -0
- package/plugin/expose-entry.mjs +86 -0
- package/plugin/find-entry.mjs +111 -0
- package/plugin/mina-runtime.mjs +58 -0
- package/plugin/sfc-split.mjs +102 -0
- package/plugin.mjs +96 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Eric
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
package/helper/hooks.mjs
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { toJSONString } from '@into-mini/sfc-transformer/utils.mjs';
|
|
2
|
+
|
|
3
|
+
import { readConfig } from './read.mjs';
|
|
4
|
+
|
|
5
|
+
export function createEmitFile({ PLUGIN_NAME, compilation, RawSource }) {
|
|
6
|
+
return (name, content) => {
|
|
7
|
+
compilation.hooks.processAssets.tap(
|
|
8
|
+
{
|
|
9
|
+
name: PLUGIN_NAME,
|
|
10
|
+
stage: compilation.constructor.PROCESS_ASSETS_STAGE_ADDITIONAL,
|
|
11
|
+
},
|
|
12
|
+
() => {
|
|
13
|
+
compilation.emitAsset(
|
|
14
|
+
name,
|
|
15
|
+
new RawSource(
|
|
16
|
+
typeof content === 'string' ? content : toJSONString(content),
|
|
17
|
+
),
|
|
18
|
+
);
|
|
19
|
+
},
|
|
20
|
+
);
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function readAndTrack(compiler, compilation) {
|
|
25
|
+
return (name) => {
|
|
26
|
+
const { filePath, config = {} } = readConfig(compiler.context, name);
|
|
27
|
+
|
|
28
|
+
if (filePath) {
|
|
29
|
+
compilation.fileDependencies.add(filePath);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
name: `${name}.json`,
|
|
34
|
+
content: config,
|
|
35
|
+
empty: Object.keys(config).length === 0,
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function createAddEntry(compiler) {
|
|
41
|
+
return (name, path) => {
|
|
42
|
+
new compiler.webpack.EntryPlugin(compiler.context, path, {
|
|
43
|
+
import: [path],
|
|
44
|
+
name,
|
|
45
|
+
// layer: 'base',
|
|
46
|
+
}).apply(compiler);
|
|
47
|
+
};
|
|
48
|
+
}
|
package/helper/index.mjs
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export const COMPONENT_ROOT = 'as-components';
|
|
2
|
+
|
|
3
|
+
function unique(...arr) {
|
|
4
|
+
return [...new Set(arr)];
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function getAllPages(config) {
|
|
8
|
+
const { entryPagePath, pages, subPackages, tabBar } = config ?? {};
|
|
9
|
+
|
|
10
|
+
const { custom = false, list = [] } = tabBar ?? {};
|
|
11
|
+
|
|
12
|
+
return unique(
|
|
13
|
+
entryPagePath,
|
|
14
|
+
...(pages ?? []),
|
|
15
|
+
...list.map(({ pagePath }) => pagePath),
|
|
16
|
+
...(subPackages ?? []).flatMap(
|
|
17
|
+
(subPackage) =>
|
|
18
|
+
(subPackage.pages || []).map((page) => `${subPackage.root}/${page}`) ||
|
|
19
|
+
[],
|
|
20
|
+
),
|
|
21
|
+
custom === true ? 'custom-tab-bar/index' : '',
|
|
22
|
+
).filter(Boolean);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function patchConfig(json) {
|
|
26
|
+
const object = structuredClone(json ?? {});
|
|
27
|
+
|
|
28
|
+
object.pages ??= [];
|
|
29
|
+
|
|
30
|
+
if (object.tabBar?.list?.length > 0) {
|
|
31
|
+
for (const tab of object.tabBar.list) {
|
|
32
|
+
if (tab.pagePath && !object.pages.includes(tab.pagePath)) {
|
|
33
|
+
object.pages.push(tab.pagePath);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
object.subPackages ??= [];
|
|
39
|
+
object.preloadRule ??= {};
|
|
40
|
+
|
|
41
|
+
for (const page of object.pages) {
|
|
42
|
+
object.preloadRule[page] ??= {};
|
|
43
|
+
|
|
44
|
+
object.preloadRule[page].network = 'all';
|
|
45
|
+
object.preloadRule[page].packages ??= [];
|
|
46
|
+
|
|
47
|
+
if (!object.preloadRule[page].packages.includes(COMPONENT_ROOT)) {
|
|
48
|
+
object.preloadRule[page].packages.push(COMPONENT_ROOT);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (
|
|
53
|
+
!object.subPackages.some((subPackage) => subPackage.root === COMPONENT_ROOT)
|
|
54
|
+
) {
|
|
55
|
+
object.subPackages.push({
|
|
56
|
+
root: COMPONENT_ROOT,
|
|
57
|
+
pages: ['fake'],
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
return object;
|
|
62
|
+
}
|
package/helper/read.mjs
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
|
|
4
|
+
import { parse as yamlParse } from 'yaml';
|
|
5
|
+
|
|
6
|
+
function tryReadFileWithParsers(base, name, ext, parser) {
|
|
7
|
+
const filePath = resolve(base, `${name}${ext}`);
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
const content = readFileSync(filePath, 'utf8');
|
|
11
|
+
|
|
12
|
+
return {
|
|
13
|
+
filePath,
|
|
14
|
+
config: (parser ? parser(content) : content) || {},
|
|
15
|
+
};
|
|
16
|
+
} catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const candidates = [
|
|
22
|
+
{ ext: '.yaml', parser: yamlParse },
|
|
23
|
+
{ ext: '.yml', parser: yamlParse },
|
|
24
|
+
{ ext: '.json', parser: JSON.parse },
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
export function readConfig(base, name) {
|
|
28
|
+
for (const { ext, parser } of candidates) {
|
|
29
|
+
const result = tryReadFileWithParsers(base, name, ext, parser);
|
|
30
|
+
|
|
31
|
+
if (result !== false) {
|
|
32
|
+
return result;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
return false;
|
|
37
|
+
}
|
package/helper/utils.mjs
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { join, relative, resolve } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
|
|
5
|
+
import { toJSONString } from '@into-mini/sfc-transformer/utils.mjs';
|
|
6
|
+
import slash from 'slash';
|
|
7
|
+
|
|
8
|
+
function createShortHash(input) {
|
|
9
|
+
return createHash('sha256').update(input).digest('hex').slice(0, 8);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function reach(path) {
|
|
13
|
+
return fileURLToPath(import.meta.resolve(path));
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function handleImport({
|
|
17
|
+
toThis,
|
|
18
|
+
addSmartEntry,
|
|
19
|
+
componentRoot,
|
|
20
|
+
context,
|
|
21
|
+
rootContext,
|
|
22
|
+
maps,
|
|
23
|
+
callback,
|
|
24
|
+
}) {
|
|
25
|
+
if (Object.keys(maps).length > 0) {
|
|
26
|
+
for (const [name, path] of Object.entries(maps)) {
|
|
27
|
+
if (path.endsWith('.vue') && !path.startsWith('plugin://')) {
|
|
28
|
+
try {
|
|
29
|
+
const absolutePath = slash(
|
|
30
|
+
path.startsWith('.') ? resolve(context, path) : reach(path),
|
|
31
|
+
);
|
|
32
|
+
const relativePath = slash(relative(rootContext, absolutePath));
|
|
33
|
+
const hack = relativePath.startsWith('..');
|
|
34
|
+
const entryName = hack
|
|
35
|
+
? [
|
|
36
|
+
componentRoot,
|
|
37
|
+
absolutePath
|
|
38
|
+
.split('/')
|
|
39
|
+
.slice(-2)
|
|
40
|
+
.join('/')
|
|
41
|
+
.replace(/\.vue$/, ''),
|
|
42
|
+
createShortHash(slash(relativePath)),
|
|
43
|
+
].join('/')
|
|
44
|
+
: relativePath.replace(/\.vue$/, '');
|
|
45
|
+
const placer = toThis(entryName);
|
|
46
|
+
callback({
|
|
47
|
+
name,
|
|
48
|
+
placer,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
const entryPath = relativePath.startsWith('..')
|
|
52
|
+
? absolutePath
|
|
53
|
+
: `./${relativePath}`;
|
|
54
|
+
|
|
55
|
+
this.addDependency(resolve(absolutePath));
|
|
56
|
+
this.addMissingDependency(resolve(absolutePath));
|
|
57
|
+
addSmartEntry({
|
|
58
|
+
name: entryName,
|
|
59
|
+
path: entryPath,
|
|
60
|
+
});
|
|
61
|
+
} catch (error) {
|
|
62
|
+
console.error(error);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export default function loader(source, map, meta) {
|
|
70
|
+
this.cacheable();
|
|
71
|
+
const callback = this.async();
|
|
72
|
+
const { componentRoot } = this.getOptions();
|
|
73
|
+
const { entryName: thisEntryName } = this;
|
|
74
|
+
const resourcePath = slash(this.resourcePath);
|
|
75
|
+
const { paths, config, script } = this.processSfcFile({
|
|
76
|
+
source,
|
|
77
|
+
resourcePath,
|
|
78
|
+
});
|
|
79
|
+
const { rootContext, context } = this;
|
|
80
|
+
|
|
81
|
+
for (const path of paths) {
|
|
82
|
+
const filePath = join(rootContext, path);
|
|
83
|
+
this.addDependency(filePath);
|
|
84
|
+
this.addMissingDependency(filePath);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function toThis(entryName) {
|
|
88
|
+
return slash(relative(`/${thisEntryName}/..`, `/${entryName}`));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const addSmartEntry = (io) => {
|
|
92
|
+
this.addSmartEntry(io);
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
if (config?.usingComponents) {
|
|
96
|
+
handleImport.bind(this)({
|
|
97
|
+
toThis,
|
|
98
|
+
addSmartEntry,
|
|
99
|
+
componentRoot,
|
|
100
|
+
context,
|
|
101
|
+
rootContext,
|
|
102
|
+
maps: config.usingComponents,
|
|
103
|
+
callback({ name, placer }) {
|
|
104
|
+
config.usingComponents[name] = placer;
|
|
105
|
+
|
|
106
|
+
if (placer.includes(componentRoot)) {
|
|
107
|
+
config.componentPlaceholder ??= {};
|
|
108
|
+
config.componentPlaceholder[name] = 'view';
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (config?.componentGenerics) {
|
|
115
|
+
handleImport.bind(this)({
|
|
116
|
+
toThis,
|
|
117
|
+
addSmartEntry,
|
|
118
|
+
componentRoot,
|
|
119
|
+
context,
|
|
120
|
+
rootContext,
|
|
121
|
+
maps: Object.fromEntries(
|
|
122
|
+
Object.entries(config.componentGenerics)
|
|
123
|
+
.filter(([_, item]) => item?.default)
|
|
124
|
+
.map(([key, item]) => [key, item.default]),
|
|
125
|
+
),
|
|
126
|
+
callback({ name, placer }) {
|
|
127
|
+
config.componentGenerics[name].default = placer;
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const file = [
|
|
133
|
+
...paths
|
|
134
|
+
.map((path) => relative(`${resourcePath}/..`, path))
|
|
135
|
+
.map((path) => `import "./${path}";`),
|
|
136
|
+
script,
|
|
137
|
+
].join('\n');
|
|
138
|
+
this.emitFile(`${thisEntryName}.json`, toJSONString(config));
|
|
139
|
+
callback(null, file, map, meta);
|
|
140
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@into-mini/sfc-split-plugin",
|
|
3
|
+
"version": "0.0.0",
|
|
4
|
+
"license": "MIT",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"loader",
|
|
7
|
+
"mini-program",
|
|
8
|
+
"miniprogram",
|
|
9
|
+
"sfc",
|
|
10
|
+
"vue",
|
|
11
|
+
"weapp",
|
|
12
|
+
"webpack",
|
|
13
|
+
"webpack-plugin",
|
|
14
|
+
"wxml"
|
|
15
|
+
],
|
|
16
|
+
"homepage": "https://github.com/into-mini/into-mini/tree/master/packages/sfc-split-plugin",
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/into-mini/into-mini.git",
|
|
20
|
+
"directory": "packages/sfc-split-plugin"
|
|
21
|
+
},
|
|
22
|
+
"bugs": {
|
|
23
|
+
"url": "https://github.com/into-mini/into-mini/issues"
|
|
24
|
+
},
|
|
25
|
+
"main": "plugin.mjs",
|
|
26
|
+
"files": [
|
|
27
|
+
"**/*.json",
|
|
28
|
+
"**/*.mjs"
|
|
29
|
+
],
|
|
30
|
+
"type": "module",
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"slash": "^5.1.0",
|
|
33
|
+
"webpack-virtual-modules": "^0.6.2",
|
|
34
|
+
"yaml": "^2.8.2",
|
|
35
|
+
"yaml-patch-loader": "^0.1.0",
|
|
36
|
+
"@into-mini/clsx": "^0.1.0",
|
|
37
|
+
"@into-mini/sfc-transformer": "^0.5.12",
|
|
38
|
+
"@into-mini/wxml-loader": "^0.0.0"
|
|
39
|
+
},
|
|
40
|
+
"peerDependencies": {
|
|
41
|
+
"@babel/core": "^7.28.5",
|
|
42
|
+
"webpack": "^5.103.0"
|
|
43
|
+
},
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=22.18.0"
|
|
46
|
+
},
|
|
47
|
+
"publishConfig": {
|
|
48
|
+
"access": "public",
|
|
49
|
+
"registry": "https://registry.npmjs.org/"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
export class AddEntryPlugin {
|
|
2
|
+
PLUGIN_NAME = 'AddEntryPlugin';
|
|
3
|
+
|
|
4
|
+
#addSmartEntry({ name, path, layer }) {
|
|
5
|
+
if (this.compiler.__entries__.get(name) !== path) {
|
|
6
|
+
this.compiler.__entries__.set(name, { path, layer });
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
#addEntries(compilation) {
|
|
11
|
+
const { compiler } = this;
|
|
12
|
+
|
|
13
|
+
const { createDependency } = compiler.webpack.EntryPlugin;
|
|
14
|
+
|
|
15
|
+
compilation.hooks.buildModule.tap(this.PLUGIN_NAME, () => {
|
|
16
|
+
for (const [name, { path, layer }] of compiler.__entries__.entries()) {
|
|
17
|
+
compilation.addEntry(
|
|
18
|
+
compiler.context,
|
|
19
|
+
createDependency(path, { name }),
|
|
20
|
+
{
|
|
21
|
+
name,
|
|
22
|
+
import: [path],
|
|
23
|
+
layer,
|
|
24
|
+
},
|
|
25
|
+
(err) => {
|
|
26
|
+
if (err) {
|
|
27
|
+
throw err;
|
|
28
|
+
} else {
|
|
29
|
+
compilation.fileDependencies.add(path);
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
#expose(compiler) {
|
|
38
|
+
this.compiler = compiler;
|
|
39
|
+
|
|
40
|
+
const { PLUGIN_NAME } = this;
|
|
41
|
+
|
|
42
|
+
const {
|
|
43
|
+
NormalModule: { getCompilationHooks },
|
|
44
|
+
} = compiler.webpack;
|
|
45
|
+
|
|
46
|
+
Object.defineProperty(compiler, 'addSmartEntry', {
|
|
47
|
+
enumerable: true,
|
|
48
|
+
configurable: false,
|
|
49
|
+
value: (options) => {
|
|
50
|
+
this.#addSmartEntry(options);
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
|
|
55
|
+
getCompilationHooks(compilation).loader.tap(
|
|
56
|
+
PLUGIN_NAME,
|
|
57
|
+
(loaderContext) => {
|
|
58
|
+
Object.defineProperty(loaderContext, 'addSmartEntry', {
|
|
59
|
+
enumerable: true,
|
|
60
|
+
configurable: false,
|
|
61
|
+
value: (options) => {
|
|
62
|
+
this.#addSmartEntry(options);
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
},
|
|
66
|
+
);
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
apply(compiler) {
|
|
71
|
+
this.#expose(compiler);
|
|
72
|
+
|
|
73
|
+
const { PLUGIN_NAME } = this;
|
|
74
|
+
|
|
75
|
+
compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
|
|
76
|
+
this.#addEntries(compilation);
|
|
77
|
+
});
|
|
78
|
+
compiler.hooks.thisCompilation.tap(PLUGIN_NAME, (compilation) => {
|
|
79
|
+
this.#addEntries(compilation);
|
|
80
|
+
});
|
|
81
|
+
compiler.hooks.make.tap(PLUGIN_NAME, (compilation) => {
|
|
82
|
+
this.#addEntries(compilation);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { extname, join, relative } from 'node:path';
|
|
3
|
+
import { fileURLToPath } from 'node:url';
|
|
4
|
+
|
|
5
|
+
import { CLSX_PLACEHOLDER } from '@into-mini/sfc-transformer/utils.mjs';
|
|
6
|
+
import slash from 'slash';
|
|
7
|
+
|
|
8
|
+
// WXS文件输出路径
|
|
9
|
+
const WXS_FILENAME = 'wxs/clsx.wxs';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 将clsx.wxs文件添加到编译结果中,并替换WXML文件中的占位符。
|
|
13
|
+
* 只在发现CLSX_PLACEHOLDER时添加wxs文件,且只添加一次。
|
|
14
|
+
*/
|
|
15
|
+
export class AddWxsPlugin {
|
|
16
|
+
PLUGIN_NAME = 'AddWxsPlugin';
|
|
17
|
+
|
|
18
|
+
apply(compiler) {
|
|
19
|
+
const { RawSource } = compiler.webpack.sources;
|
|
20
|
+
|
|
21
|
+
compiler.hooks.compilation.tap(this.PLUGIN_NAME, (compilation) => {
|
|
22
|
+
compilation.hooks.processAssets.tap(
|
|
23
|
+
{
|
|
24
|
+
name: this.PLUGIN_NAME,
|
|
25
|
+
stage: compilation.constructor.PROCESS_ASSETS_STAGE_ADDITIONAL,
|
|
26
|
+
},
|
|
27
|
+
(assets) => this.#processAssets(assets, compilation, RawSource),
|
|
28
|
+
);
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
#processAssets(assets, compilation, RawSource) {
|
|
33
|
+
// 处理所有wxml文件
|
|
34
|
+
for (const [filename, source] of Object.entries(assets)) {
|
|
35
|
+
if (extname(filename) === '.wxml') {
|
|
36
|
+
const content = source.source().toString();
|
|
37
|
+
|
|
38
|
+
if (content.includes(CLSX_PLACEHOLDER)) {
|
|
39
|
+
this.#addWxsFile(compilation, RawSource);
|
|
40
|
+
|
|
41
|
+
this.#replaceSource(compilation, RawSource, {
|
|
42
|
+
filename,
|
|
43
|
+
content,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
#replaceSource(compilation, RawSource, { filename, content }) {
|
|
51
|
+
const relativePath = slash(relative(join(filename, '..'), WXS_FILENAME));
|
|
52
|
+
const newContent = content.replace(CLSX_PLACEHOLDER, relativePath);
|
|
53
|
+
compilation.updateAsset(filename, new RawSource(newContent));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
#addWxsFile(compilation, RawSource) {
|
|
57
|
+
const wxsPath = import.meta.resolve('@into-mini/clsx/index.wxs');
|
|
58
|
+
const wxsContent = readFileSync(fileURLToPath(wxsPath), 'utf8');
|
|
59
|
+
|
|
60
|
+
compilation.emitAsset(WXS_FILENAME, new RawSource(wxsContent));
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { fileURLToPath } from 'node:url';
|
|
2
|
+
|
|
3
|
+
import { patchConfig } from '../helper/index.mjs';
|
|
4
|
+
import { configKeys } from '../helper/utils.mjs';
|
|
5
|
+
|
|
6
|
+
function reach(path) {
|
|
7
|
+
return fileURLToPath(import.meta.resolve(path));
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const emptyJSON = reach('../helper/empty.json');
|
|
11
|
+
const yamlLoader = reach('yaml-patch-loader');
|
|
12
|
+
|
|
13
|
+
export class CopyConfigPlugin {
|
|
14
|
+
constructor({ type = false } = {}) {
|
|
15
|
+
this.type = type;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
addConfigSmartEntry({
|
|
19
|
+
layer,
|
|
20
|
+
from = layer,
|
|
21
|
+
name = layer,
|
|
22
|
+
filename = from,
|
|
23
|
+
options,
|
|
24
|
+
}) {
|
|
25
|
+
const path = `./${from}.yaml`;
|
|
26
|
+
|
|
27
|
+
this.compiler.options.entry[name] = {
|
|
28
|
+
import: [path],
|
|
29
|
+
layer,
|
|
30
|
+
runtime: false,
|
|
31
|
+
filename,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
this.compiler.options.resolve.fallback[path] = emptyJSON;
|
|
35
|
+
|
|
36
|
+
this.compiler.options.module.rules.push({
|
|
37
|
+
issuerLayer: layer,
|
|
38
|
+
loader: yamlLoader,
|
|
39
|
+
type: 'asset/resource',
|
|
40
|
+
generator: {
|
|
41
|
+
filename: `${filename}.json`,
|
|
42
|
+
},
|
|
43
|
+
options,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
apply(compiler) {
|
|
48
|
+
const { type } = this;
|
|
49
|
+
this.compiler = compiler;
|
|
50
|
+
|
|
51
|
+
if (type) {
|
|
52
|
+
this.addConfigSmartEntry({
|
|
53
|
+
layer: configKeys.project,
|
|
54
|
+
options: {
|
|
55
|
+
modify: (json) => ({
|
|
56
|
+
srcMiniprogramRoot: '',
|
|
57
|
+
miniprogramRoot: '',
|
|
58
|
+
pluginRoot: '',
|
|
59
|
+
...json,
|
|
60
|
+
compileType: type,
|
|
61
|
+
}),
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
this.addConfigSmartEntry({
|
|
66
|
+
layer: configKeys.projectPrivate,
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
if (this.type === 'miniprogram') {
|
|
70
|
+
this.addConfigSmartEntry({
|
|
71
|
+
layer: configKeys.app,
|
|
72
|
+
from: 'app',
|
|
73
|
+
options: {
|
|
74
|
+
modify: patchConfig,
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
this.compiler.options.entry.app = {
|
|
79
|
+
import: ['./app'],
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
// this.compiler.options.entry.__hack__ = {
|
|
83
|
+
// import: [reach('../helper/hack.hack')],
|
|
84
|
+
// layer: configKeys.hack,
|
|
85
|
+
// };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { COMPONENT_ROOT } from '../helper/index.mjs';
|
|
2
|
+
|
|
3
|
+
const files = {
|
|
4
|
+
'/fake.json': '{}',
|
|
5
|
+
'/fake.js': '/**用于创建分包的假页面**/',
|
|
6
|
+
'/fake.wxml': '<!--用于创建分包的假页面-->',
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export class EmitFakePlugin {
|
|
10
|
+
PLUGIN_NAME = 'EmitFakePlugin';
|
|
11
|
+
|
|
12
|
+
apply(compiler) {
|
|
13
|
+
const {
|
|
14
|
+
sources: { RawSource },
|
|
15
|
+
Compilation: { PROCESS_ASSETS_STAGE_ADDITIONAL },
|
|
16
|
+
} = compiler.webpack;
|
|
17
|
+
|
|
18
|
+
compiler.hooks.make.tap(this.PLUGIN_NAME, (compilation) => {
|
|
19
|
+
compilation.hooks.processAssets.tap(
|
|
20
|
+
{
|
|
21
|
+
name: this.PLUGIN_NAME,
|
|
22
|
+
stage: PROCESS_ASSETS_STAGE_ADDITIONAL,
|
|
23
|
+
},
|
|
24
|
+
() => {
|
|
25
|
+
for (const [path, content] of Object.entries(files)) {
|
|
26
|
+
compilation.emitAsset(
|
|
27
|
+
COMPONENT_ROOT + path,
|
|
28
|
+
new RawSource(content),
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import slash from 'slash';
|
|
2
|
+
const PLUGIN_NAME = 'ExposeEntryNamePlugin';
|
|
3
|
+
export class ExposeEntryNamePlugin {
|
|
4
|
+
getEntryNameFromEntries(compilation, module) {
|
|
5
|
+
const { moduleGraph, entries } = compilation;
|
|
6
|
+
for (const [name, io] of entries) {
|
|
7
|
+
for (const dep of io.dependencies) {
|
|
8
|
+
const entryModule = moduleGraph.getModule(dep);
|
|
9
|
+
if (entryModule) {
|
|
10
|
+
if (
|
|
11
|
+
// @ts-expect-error ------------
|
|
12
|
+
entryModule.request && // @ts-expect-error ------------
|
|
13
|
+
slash(entryModule.request) === slash(module.request)
|
|
14
|
+
) {
|
|
15
|
+
return name;
|
|
16
|
+
}
|
|
17
|
+
if (
|
|
18
|
+
// @ts-expect-error ------------
|
|
19
|
+
entryModule?.resource && // @ts-expect-error ------------
|
|
20
|
+
slash(entryModule?.resource) === slash(module.resource)
|
|
21
|
+
) {
|
|
22
|
+
return name;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return '';
|
|
28
|
+
}
|
|
29
|
+
getEntryNameFromPathData(compilation, pathData) {
|
|
30
|
+
const mod = pathData.module;
|
|
31
|
+
const graph = pathData.chunkGraph;
|
|
32
|
+
if (mod && graph) {
|
|
33
|
+
const [entryModule] = graph
|
|
34
|
+
.getModuleChunks(mod)
|
|
35
|
+
.map((chunk) => [...graph.getChunkEntryModulesIterable(chunk)][0]);
|
|
36
|
+
if (entryModule) {
|
|
37
|
+
return this.getEntryNameFromEntries(compilation, entryModule);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
return '';
|
|
41
|
+
}
|
|
42
|
+
apply(compiler) {
|
|
43
|
+
const {
|
|
44
|
+
NormalModule: { getCompilationHooks },
|
|
45
|
+
} = compiler.webpack;
|
|
46
|
+
compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
|
|
47
|
+
compilation.hooks.assetPath.tap(PLUGIN_NAME, (path, pathData) => {
|
|
48
|
+
if (path.includes('[entry]')) {
|
|
49
|
+
const entryName = this.getEntryNameFromPathData(
|
|
50
|
+
compilation,
|
|
51
|
+
pathData,
|
|
52
|
+
);
|
|
53
|
+
return entryName
|
|
54
|
+
? path.replaceAll('[entry]', entryName)
|
|
55
|
+
: path.replaceAll('[entry]', '[hash:8]');
|
|
56
|
+
}
|
|
57
|
+
return path;
|
|
58
|
+
});
|
|
59
|
+
getCompilationHooks(compilation).loader.tap(
|
|
60
|
+
PLUGIN_NAME,
|
|
61
|
+
(loaderContext, module) => {
|
|
62
|
+
Object.defineProperty(loaderContext, 'entryName', {
|
|
63
|
+
enumerable: true,
|
|
64
|
+
configurable: false,
|
|
65
|
+
get: () => {
|
|
66
|
+
return this.getEntryNameFromEntries(compilation, module);
|
|
67
|
+
},
|
|
68
|
+
});
|
|
69
|
+
},
|
|
70
|
+
);
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import slash from 'slash';
|
|
2
|
+
const PLUGIN_NAME = 'ExposeEntryNamePlugin';
|
|
3
|
+
export class ExposeEntryNamePlugin {
|
|
4
|
+
getEntryNameFromChunk(chunk) {
|
|
5
|
+
if (!chunk?.groupsIterable) {
|
|
6
|
+
return '';
|
|
7
|
+
}
|
|
8
|
+
for (const group of chunk.groupsIterable) {
|
|
9
|
+
if (group.isInitial()) {
|
|
10
|
+
return group.name;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return '';
|
|
14
|
+
}
|
|
15
|
+
getEntryNameFromEntries(compilation, module) {
|
|
16
|
+
const { moduleGraph, entries } = compilation;
|
|
17
|
+
for (const [name, io] of entries) {
|
|
18
|
+
for (const dep of io.dependencies) {
|
|
19
|
+
const entryModule = moduleGraph.getModule(dep);
|
|
20
|
+
if (entryModule) {
|
|
21
|
+
if (
|
|
22
|
+
// @ts-expect-error ------------
|
|
23
|
+
entryModule.request && // @ts-expect-error ------------
|
|
24
|
+
slash(entryModule.request) === slash(module.request)
|
|
25
|
+
) {
|
|
26
|
+
return name;
|
|
27
|
+
}
|
|
28
|
+
if (
|
|
29
|
+
// @ts-expect-error ------------
|
|
30
|
+
entryModule?.resource && // @ts-expect-error ------------
|
|
31
|
+
slash(entryModule?.resource) === slash(module.resource)
|
|
32
|
+
) {
|
|
33
|
+
return name;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return '';
|
|
39
|
+
}
|
|
40
|
+
getEntryNameFromPathData(pathData) {
|
|
41
|
+
if (pathData?.chunk) {
|
|
42
|
+
const entryName = this.getEntryNameFromChunk(pathData.chunk);
|
|
43
|
+
if (entryName) {
|
|
44
|
+
return entryName;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
if (pathData?.module && pathData?.chunkGraph) {
|
|
48
|
+
const chunks = pathData.chunkGraph.getModuleChunks(pathData.module);
|
|
49
|
+
for (const chunk of chunks) {
|
|
50
|
+
const entryName = this.getEntryNameFromChunk(chunk);
|
|
51
|
+
if (entryName) {
|
|
52
|
+
return entryName;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return '';
|
|
57
|
+
}
|
|
58
|
+
apply(compiler) {
|
|
59
|
+
const {
|
|
60
|
+
NormalModule: { getCompilationHooks },
|
|
61
|
+
} = compiler.webpack;
|
|
62
|
+
compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
|
|
63
|
+
compilation.hooks.assetPath.tap(PLUGIN_NAME, (path, pathData) => {
|
|
64
|
+
if (path.includes('[entry]')) {
|
|
65
|
+
const entryName = this.getEntryNameFromPathData(pathData);
|
|
66
|
+
return entryName
|
|
67
|
+
? path.replaceAll('[entry]', entryName)
|
|
68
|
+
: path.replaceAll('[entry]', '[hash:8]');
|
|
69
|
+
}
|
|
70
|
+
return path;
|
|
71
|
+
});
|
|
72
|
+
getCompilationHooks(compilation).loader.tap(
|
|
73
|
+
PLUGIN_NAME,
|
|
74
|
+
(loaderContext, module) => {
|
|
75
|
+
Object.defineProperty(loaderContext, 'entryName', {
|
|
76
|
+
enumerable: true,
|
|
77
|
+
configurable: false,
|
|
78
|
+
get: () => {
|
|
79
|
+
return this.getEntryNameFromEntries(compilation, module);
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
},
|
|
83
|
+
);
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { extname } from 'node:path';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
createAddEntry,
|
|
5
|
+
createEmitFile,
|
|
6
|
+
readAndTrack,
|
|
7
|
+
} from '../helper/hooks.mjs';
|
|
8
|
+
import { getAllPages } from '../helper/index.mjs';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Webpack插件,用于处理小程序和插件的入口文件
|
|
12
|
+
*/
|
|
13
|
+
export class FindEntryPlugin {
|
|
14
|
+
PLUGIN_NAME = 'FindEntryPlugin';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @param {Object} options - 插件配置选项
|
|
18
|
+
* @param {string} [options.type=false] - 项目类型,可选值:'miniprogram'或'plugin'
|
|
19
|
+
*/
|
|
20
|
+
constructor({ type = false } = {}) {
|
|
21
|
+
this.type = type;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 处理Vue页面入口
|
|
26
|
+
* @private
|
|
27
|
+
*/
|
|
28
|
+
#handleVuePages(params, pages, basePath = '.') {
|
|
29
|
+
const { addEntry, compilation } = params;
|
|
30
|
+
|
|
31
|
+
for (const page of pages) {
|
|
32
|
+
const source = `${basePath}/${page}.vue`;
|
|
33
|
+
addEntry(page, source);
|
|
34
|
+
compilation.fileDependencies.add(source);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* 处理插件页面和组件
|
|
40
|
+
* @private
|
|
41
|
+
*/
|
|
42
|
+
#handlePluginSection(params, config, section) {
|
|
43
|
+
const entries = config[section];
|
|
44
|
+
|
|
45
|
+
if (!entries) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
Object.entries(entries).forEach(([key, path]) => {
|
|
50
|
+
if (typeof path === 'string' && extname(path) === '.vue') {
|
|
51
|
+
const source = `${section}/${key}/index`;
|
|
52
|
+
params.addEntry(source, path);
|
|
53
|
+
params.compilation.fileDependencies.add(path);
|
|
54
|
+
|
|
55
|
+
if (section === 'pages') {
|
|
56
|
+
config.pages = config.pages || {};
|
|
57
|
+
config.pages[key] = source;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* 处理配置和入口
|
|
65
|
+
* @private
|
|
66
|
+
*/
|
|
67
|
+
#processConfig(params, configType) {
|
|
68
|
+
const { readFrom, emitFile, addEntry, compilation } = params;
|
|
69
|
+
const { content: config, name } = readFrom(
|
|
70
|
+
configType === 'miniprogram' ? 'app' : configType,
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
if (configType === 'miniprogram') {
|
|
74
|
+
this.#handleVuePages(params, getAllPages(config));
|
|
75
|
+
} else if (configType === 'plugin') {
|
|
76
|
+
if (config.main) {
|
|
77
|
+
addEntry('main', config.main);
|
|
78
|
+
compilation.fileDependencies.add(config.main);
|
|
79
|
+
config.main = 'main.js';
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
this.#handlePluginSection(params, config, 'pages');
|
|
83
|
+
this.#handlePluginSection(params, config, 'publicComponents');
|
|
84
|
+
emitFile(name, config);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
apply(compiler) {
|
|
89
|
+
const {
|
|
90
|
+
sources: { RawSource },
|
|
91
|
+
} = compiler.webpack;
|
|
92
|
+
|
|
93
|
+
const addEntry = createAddEntry(compiler);
|
|
94
|
+
|
|
95
|
+
// 处理入口文件和配置
|
|
96
|
+
compiler.hooks.compilation.tap(this.PLUGIN_NAME, (compilation) => {
|
|
97
|
+
const params = {
|
|
98
|
+
compilation,
|
|
99
|
+
addEntry,
|
|
100
|
+
emitFile: createEmitFile({
|
|
101
|
+
PLUGIN_NAME: this.PLUGIN_NAME,
|
|
102
|
+
compilation,
|
|
103
|
+
RawSource,
|
|
104
|
+
}),
|
|
105
|
+
readFrom: readAndTrack(compiler, compilation),
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
this.#processConfig(params, this.type);
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { dirname, relative, sep } from 'node:path';
|
|
2
|
+
|
|
3
|
+
const PLUGIN_NAME = 'MinaRuntimeWebpackPlugin';
|
|
4
|
+
|
|
5
|
+
export class MinaRuntimeWebpackPlugin {
|
|
6
|
+
// 格式化依赖路径为require语句
|
|
7
|
+
#formatRequire = (from, to) =>
|
|
8
|
+
`require('./${relative(dirname(from), to).split(sep).join('/')}');\n`;
|
|
9
|
+
|
|
10
|
+
// 收集并生成所有依赖的require语句
|
|
11
|
+
#generateDependencies = (chunk) => {
|
|
12
|
+
if (!chunk?.name) {
|
|
13
|
+
return '';
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
let result = '';
|
|
17
|
+
|
|
18
|
+
for (const { chunks } of chunk.groupsIterable) {
|
|
19
|
+
for (const depChunk of chunks) {
|
|
20
|
+
if (depChunk !== chunk && depChunk.name) {
|
|
21
|
+
result += this.#formatRequire(chunk.name, depChunk.name);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return result;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
apply(compiler) {
|
|
30
|
+
compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
|
|
31
|
+
const {
|
|
32
|
+
javascript: { JavascriptModulesPlugin },
|
|
33
|
+
sources: { ConcatSource },
|
|
34
|
+
} = compiler.webpack;
|
|
35
|
+
|
|
36
|
+
JavascriptModulesPlugin.getCompilationHooks(compilation).renderChunk.tap(
|
|
37
|
+
PLUGIN_NAME,
|
|
38
|
+
(source, { chunk }) => {
|
|
39
|
+
if (
|
|
40
|
+
!chunk ||
|
|
41
|
+
!compilation.chunkGraph.getNumberOfEntryModules(chunk)
|
|
42
|
+
) {
|
|
43
|
+
// 跳过非入口模块
|
|
44
|
+
return source;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const deps = this.#generateDependencies(chunk);
|
|
48
|
+
|
|
49
|
+
if (!deps) {
|
|
50
|
+
return source;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return new ConcatSource(deps, source);
|
|
54
|
+
},
|
|
55
|
+
);
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
|
|
3
|
+
import { parse } from '@into-mini/sfc-transformer';
|
|
4
|
+
import slash from 'slash';
|
|
5
|
+
import VirtualModulesPlugin from 'webpack-virtual-modules';
|
|
6
|
+
|
|
7
|
+
export class SfcSplitPlugin extends VirtualModulesPlugin {
|
|
8
|
+
PLUGIN_NAME = 'SfcSplitPlugin';
|
|
9
|
+
|
|
10
|
+
constructor({ tagMatcher, preserveTap }) {
|
|
11
|
+
super();
|
|
12
|
+
this.tagMatcher = tagMatcher;
|
|
13
|
+
this.preserveTap = preserveTap;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
apply(compiler) {
|
|
17
|
+
this.#expose(compiler);
|
|
18
|
+
|
|
19
|
+
super.apply(compiler);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
#expose(compiler) {
|
|
23
|
+
const { PLUGIN_NAME } = this;
|
|
24
|
+
|
|
25
|
+
const {
|
|
26
|
+
NormalModule: { getCompilationHooks },
|
|
27
|
+
} = compiler.webpack;
|
|
28
|
+
|
|
29
|
+
compiler.hooks.compilation.tap(PLUGIN_NAME, (compilation) => {
|
|
30
|
+
getCompilationHooks(compilation).loader.tap(
|
|
31
|
+
PLUGIN_NAME,
|
|
32
|
+
(loaderContext) => {
|
|
33
|
+
Object.defineProperty(loaderContext, 'processSfcFile', {
|
|
34
|
+
enumerable: true,
|
|
35
|
+
configurable: false,
|
|
36
|
+
value: (options) => {
|
|
37
|
+
return this.#processSfcFile(options);
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
},
|
|
41
|
+
);
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
#inject(resourcePath, ext, content) {
|
|
46
|
+
const src = path.resolve(resourcePath.replace(/\.vue$/, ext));
|
|
47
|
+
|
|
48
|
+
super.writeModule(src, content);
|
|
49
|
+
|
|
50
|
+
return src;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
#injectStyle(resourcePath, id, style) {
|
|
54
|
+
return this.#inject(
|
|
55
|
+
resourcePath,
|
|
56
|
+
`-${id}.${style.lang ?? 'css'}`,
|
|
57
|
+
style.content,
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
#injectStyles(resourcePath, styles) {
|
|
62
|
+
const io = [];
|
|
63
|
+
|
|
64
|
+
const css = styles?.length > 0 ? styles : [];
|
|
65
|
+
|
|
66
|
+
css.forEach((style, idx) => {
|
|
67
|
+
if (style?.content) {
|
|
68
|
+
const src = this.#injectStyle(resourcePath, idx, style);
|
|
69
|
+
io.push(src);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
return io;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
#injectTemplate(resourcePath, tpl) {
|
|
77
|
+
return this.#inject(resourcePath, '.wxml', tpl);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
#processSfcFile({ source, resourcePath }) {
|
|
81
|
+
const { tagMatcher, preserveTap } = this;
|
|
82
|
+
|
|
83
|
+
const { tpl, styles, code, config } = parse(source, {
|
|
84
|
+
tagMatcher,
|
|
85
|
+
preserveTap,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const paths = [];
|
|
89
|
+
|
|
90
|
+
const wxml = this.#injectTemplate(resourcePath, tpl);
|
|
91
|
+
paths.push(wxml);
|
|
92
|
+
|
|
93
|
+
const css = this.#injectStyles(resourcePath, styles);
|
|
94
|
+
paths.push(...css);
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
config,
|
|
98
|
+
paths: paths.map((src) => slash(src)),
|
|
99
|
+
script: code,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
package/plugin.mjs
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/* eslint-disable no-param-reassign */
|
|
2
|
+
import { fileURLToPath } from 'node:url';
|
|
3
|
+
|
|
4
|
+
import { COMPONENT_ROOT } from './helper/index.mjs';
|
|
5
|
+
import { configKeys } from './helper/utils.mjs';
|
|
6
|
+
import { AddEntryPlugin } from './plugin/add-entry.mjs';
|
|
7
|
+
import { AddWxsPlugin } from './plugin/add-wxs.mjs';
|
|
8
|
+
import { CopyConfigPlugin } from './plugin/copy-config.mjs';
|
|
9
|
+
import { EmitFakePlugin } from './plugin/emit-fake.mjs';
|
|
10
|
+
import { ExposeEntryNamePlugin } from './plugin/expose-entry.mjs';
|
|
11
|
+
import { FindEntryPlugin } from './plugin/find-entry.mjs';
|
|
12
|
+
import { SfcSplitPlugin } from './plugin/sfc-split.mjs';
|
|
13
|
+
import { MinaRuntimeWebpackPlugin } from './plugin/mina-runtime.mjs';
|
|
14
|
+
|
|
15
|
+
function reach(path) {
|
|
16
|
+
return fileURLToPath(import.meta.resolve(path));
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class AllInOnePlugin {
|
|
20
|
+
constructor({ type = false, tagMatcher, preserveTap } = {}) {
|
|
21
|
+
this.type = type;
|
|
22
|
+
this.tagMatcher = tagMatcher;
|
|
23
|
+
this.preserveTap = preserveTap;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
#applyLoader(compiler) {
|
|
27
|
+
compiler.options.module.rules.push(
|
|
28
|
+
// {
|
|
29
|
+
// exclude: /\.(vue|wxml)$/,
|
|
30
|
+
// layer: 'other',
|
|
31
|
+
// },
|
|
32
|
+
{
|
|
33
|
+
test: /\.vue$/,
|
|
34
|
+
loader: reach('./loader/fake-vue-loader.mjs'),
|
|
35
|
+
options: {
|
|
36
|
+
componentRoot: COMPONENT_ROOT,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
test: /\.wxml$/,
|
|
41
|
+
type: 'asset/resource',
|
|
42
|
+
loader: reach('@into-mini/wxml-loader'),
|
|
43
|
+
generator: {
|
|
44
|
+
filename: '[entry][ext]',
|
|
45
|
+
},
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
test: /\.hack$/,
|
|
49
|
+
type: 'javascript/esm',
|
|
50
|
+
layer: configKeys.hack,
|
|
51
|
+
loader: reach('./loader/hack-entry-loader.mjs'),
|
|
52
|
+
},
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
#prepare(compiler) {
|
|
57
|
+
compiler.options.resolve.extensionAlias ??= {};
|
|
58
|
+
|
|
59
|
+
compiler.options.resolve.extensionAlias['.yaml'] = [
|
|
60
|
+
'.yaml',
|
|
61
|
+
'.yml',
|
|
62
|
+
'.json',
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
compiler.options.resolve.fallback ??= {};
|
|
66
|
+
|
|
67
|
+
if (compiler.options.entry?.main) {
|
|
68
|
+
delete compiler.options.entry.main;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
Object.assign(compiler, {
|
|
72
|
+
__entries__: new Map(),
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
apply(compiler) {
|
|
77
|
+
this.#prepare(compiler);
|
|
78
|
+
this.#applyLoader(compiler);
|
|
79
|
+
|
|
80
|
+
const { type, tagMatcher, preserveTap } = this;
|
|
81
|
+
|
|
82
|
+
if (type) {
|
|
83
|
+
new MinaRuntimeWebpackPlugin().apply(compiler);
|
|
84
|
+
new AddEntryPlugin().apply(compiler);
|
|
85
|
+
new AddWxsPlugin().apply(compiler);
|
|
86
|
+
new SfcSplitPlugin({ tagMatcher, preserveTap }).apply(compiler);
|
|
87
|
+
new ExposeEntryNamePlugin().apply(compiler);
|
|
88
|
+
new FindEntryPlugin({ type }).apply(compiler);
|
|
89
|
+
new CopyConfigPlugin({ type }).apply(compiler);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (type === 'miniprogram') {
|
|
93
|
+
new EmitFakePlugin().apply(compiler);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|