@windwalker-io/core 4.1.12 → 4.2.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/bin/release.js +2 -2
- package/core/index.d.ts +1 -0
- package/core/index.html +13 -0
- package/core/lib/main.ts +9 -0
- package/core/package.json +26 -0
- package/core/public/vite.svg +1 -0
- package/core/src/main.ts +23 -0
- package/core/src/style.css +95 -0
- package/core/src/typescript.svg +1 -0
- package/core/src/vite-env.d.ts +1 -0
- package/package.json +67 -56
- package/{postcss.config.cjs → postcss.config.js} +1 -1
- package/src/app/app.ts +50 -0
- package/src/app/index.ts +3 -0
- package/src/app/macros/defineJsModules.ts +5 -0
- package/src/app/macros/index.ts +1 -0
- package/src/asset-bundler.mjs +114 -0
- package/src/debugger/Store.vue +1 -1
- package/src/debugger/debugger.js +3 -5
- package/src/index.mjs +2 -0
- package/src/next/fusion/index.ts +2 -0
- package/src/next/fusion/plugins/assets.ts +29 -0
- package/src/next/fusion/plugins/index.ts +3 -0
- package/src/next/fusion/plugins/systemjs.ts +66 -0
- package/src/next/fusion/processors/cloneAssets.ts +81 -0
- package/src/next/fusion/processors/cssModulize.ts +105 -0
- package/src/next/fusion/processors/index.ts +4 -0
- package/src/next/fusion/processors/installVendors.ts +178 -0
- package/src/next/fusion/processors/jsModulize.ts +296 -0
- package/src/next/index.ts +2 -0
- package/src/next/utilities/asset-sync.ts +47 -0
- package/src/next/utilities/crypto.ts +11 -0
- package/src/next/utilities/fs.ts +61 -0
- package/src/next/utilities/index.ts +5 -0
- package/src/next/utilities/modules.ts +17 -0
- package/tailwind/console.tailwind.config.cjs +1 -1
- package/tailwind.config.js +10 -0
- package/vite.config.debugger.ts +94 -0
- package/vite.config.next.ts +75 -0
- package/.gulp.json +0 -7
- package/dist/debugger/100-a64696bac484f689dc7f.js +0 -2
- package/dist/debugger/100.js.map +0 -1
- package/dist/debugger/153-2f3366cb4e48e0749485.js +0 -2
- package/dist/debugger/153.js.map +0 -1
- package/dist/debugger/282-6f9f84a149fc7e52beb5.js +0 -2
- package/dist/debugger/282.js.map +0 -1
- package/dist/debugger/358-bdcf2ec8126fe2a9a049.js +0 -2
- package/dist/debugger/358.js.map +0 -1
- package/dist/debugger/416-ac63d360de6c26cab89a.js +0 -2
- package/dist/debugger/416.js.map +0 -1
- package/dist/debugger/489-e2b77a72f97d9e96f9c0.js +0 -2
- package/dist/debugger/489.js.map +0 -1
- package/dist/debugger/694-f1f22e67d91a57498639.js +0 -2
- package/dist/debugger/694.js.map +0 -1
- package/dist/debugger/941-c22b0704c67ca5058678.js +0 -2
- package/dist/debugger/941.js.map +0 -1
- package/dist/debugger/debugger.js +0 -3
- package/dist/debugger/debugger.js.LICENSE.txt +0 -13
- package/dist/debugger/main.js.map +0 -1
- package/dist/debugger/windwalker-logo-h-w.svg +0 -1
- package/dist/debugger-console.css +0 -2
- package/dist/debugger-console.css.map +0 -1
- package/dist/debugger-console.js +0 -2
- package/dist/debugger-console.js.map +0 -1
- package/dist/images/windwalker-logo-h-w.svg +0 -1
- package/fusionfile.mjs +0 -96
- package/src/debugger/webpack.config.js +0 -9
- package/tailwind.config.cjs +0 -30
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { callback, type ConfigBuilder } from '@windwalker-io/fusion-next';
|
|
2
|
+
import isGlob from 'is-glob';
|
|
3
|
+
import micromatch from 'micromatch';
|
|
4
|
+
import { normalize } from 'node:path';
|
|
5
|
+
import { relative } from 'node:path';
|
|
6
|
+
import { containsMiddleGlob, removeLastGlob, uniqId } from '../../utilities';
|
|
7
|
+
|
|
8
|
+
export function cloneAssets(patterns: Record<string, string>) {
|
|
9
|
+
return callback((taskName, builder) => {
|
|
10
|
+
const reposition = getAvailableForReposition(patterns);
|
|
11
|
+
|
|
12
|
+
handleReposition(builder, reposition);
|
|
13
|
+
|
|
14
|
+
handleCloneAssets(builder, Object.keys(patterns));
|
|
15
|
+
|
|
16
|
+
return null;
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function getAvailableForReposition(patterns: Record<string, string>) {
|
|
21
|
+
const reposition: Record<string, string> = {};
|
|
22
|
+
|
|
23
|
+
for (const from in patterns) {
|
|
24
|
+
// If clone from contains middle glob: eg. `assets/**/files`, we cannot handle the naming
|
|
25
|
+
// Let the naming options to handle it.
|
|
26
|
+
if (!containsMiddleGlob(from)) {
|
|
27
|
+
reposition[from] = patterns[from];
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return reposition;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function handleCloneAssets(builder: ConfigBuilder, clonePatterns: string[]) {
|
|
35
|
+
// An module starts `hidden:` will be ignored by fusion
|
|
36
|
+
const id = uniqId('hidden:clone-asset-') + '.js';
|
|
37
|
+
|
|
38
|
+
const task = builder.addTask(id);
|
|
39
|
+
|
|
40
|
+
builder.resolveIdCallbacks.push((src) => {
|
|
41
|
+
if (src === id) {
|
|
42
|
+
return id;
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
builder.loadCallbacks.push((src) => {
|
|
47
|
+
if (src === id) {
|
|
48
|
+
const glob = clonePatterns
|
|
49
|
+
// Replace slash to unix style
|
|
50
|
+
.map(v => v.replace(/\\/g, '/'))
|
|
51
|
+
// Glob in virtual module should start with /
|
|
52
|
+
.map(v => v.startsWith('./') || !v.startsWith('/') ? `/${v}` : v)
|
|
53
|
+
// wrap with quotes
|
|
54
|
+
.map(v => `'${v}'`)
|
|
55
|
+
// join it to string.
|
|
56
|
+
.join(', ');
|
|
57
|
+
|
|
58
|
+
return `import.meta.glob(${glob});\n`;
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function handleReposition(builder: ConfigBuilder, reposition: Record<string, string>) {
|
|
64
|
+
builder.assetFileNamesCallbacks.push((assetInfo) => {
|
|
65
|
+
const fileName = assetInfo.originalFileName!;
|
|
66
|
+
|
|
67
|
+
for (const base in reposition) {
|
|
68
|
+
if (match(fileName, base)) {
|
|
69
|
+
return normalize(reposition[base] + relative(removeLastGlob(base), fileName)).replace(/\\/g, '/');
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function match(str: string, pattern: string) {
|
|
76
|
+
if (isGlob(pattern)) {
|
|
77
|
+
return micromatch.isMatch(str, pattern);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return str.startsWith(pattern);
|
|
81
|
+
}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { stripUrlQuery } from '@/next';
|
|
2
|
+
import { type ConfigBuilder, css, type ProcessorInterface, type ProcessorPreview } from '@windwalker-io/fusion-next';
|
|
3
|
+
import { WatchTask } from '@windwalker-io/fusion-next/src/types';
|
|
4
|
+
import fg from 'fast-glob';
|
|
5
|
+
import fs from 'fs-extra';
|
|
6
|
+
import { parse } from 'node-html-parser';
|
|
7
|
+
import { normalize, resolve } from 'node:path';
|
|
8
|
+
|
|
9
|
+
export function cssModulize(entry: string, dest: string) {
|
|
10
|
+
return new CssModulizeProcessor(css(entry, dest));
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
class CssModulizeProcessor implements ProcessorInterface {
|
|
14
|
+
|
|
15
|
+
constructor(
|
|
16
|
+
protected processor: ReturnType<typeof css>,
|
|
17
|
+
protected bladePatterns: string[] = [],
|
|
18
|
+
protected cssPatterns: string[] = []
|
|
19
|
+
) {
|
|
20
|
+
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
parseBlades(...bladePatterns: (string[] | string)[]) {
|
|
24
|
+
this.bladePatterns = this.bladePatterns.concat(bladePatterns.flat());
|
|
25
|
+
|
|
26
|
+
return this;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
mergeCss(...css: (string[] | string)[]) {
|
|
30
|
+
this.cssPatterns = this.cssPatterns.concat(css.flat());
|
|
31
|
+
|
|
32
|
+
return this;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
config(taskName: string, builder: ConfigBuilder) {
|
|
36
|
+
const tasks = this.processor.config(taskName, builder);
|
|
37
|
+
const task = tasks[0];
|
|
38
|
+
const inputFile = resolve(task.input);
|
|
39
|
+
|
|
40
|
+
// get blade styles and add watches
|
|
41
|
+
const bladeFiles = fg.globSync(this.bladePatterns);
|
|
42
|
+
|
|
43
|
+
for (const file of bladeFiles) {
|
|
44
|
+
builder.watches.push({
|
|
45
|
+
file,
|
|
46
|
+
moduleFile: inputFile,
|
|
47
|
+
updateType: 'css-update',
|
|
48
|
+
} satisfies WatchTask);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
builder.loadCallbacks.push((src, options) => {
|
|
52
|
+
const file = stripUrlQuery(src);
|
|
53
|
+
|
|
54
|
+
if (normalize(file) === inputFile) {
|
|
55
|
+
const patterns = fg.globSync(
|
|
56
|
+
this.cssPatterns.map((v) => resolve(v))
|
|
57
|
+
.map(v => v.replace(/\\/g, '/'))
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
const imports = patterns
|
|
61
|
+
.map((pattern) => `@import "${pattern}";`)
|
|
62
|
+
.concat(this.parseStylesFromBlades(bladeFiles))
|
|
63
|
+
.join('\n');
|
|
64
|
+
|
|
65
|
+
let main = fs.readFileSync(file, 'utf-8');
|
|
66
|
+
|
|
67
|
+
main += `\n\n${imports}\n`;
|
|
68
|
+
|
|
69
|
+
return main;
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
return undefined;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
parseStylesFromBlades(files: string[]) {
|
|
77
|
+
return files.map((file) => {
|
|
78
|
+
const bladeText = fs.readFileSync(file, 'utf8');
|
|
79
|
+
|
|
80
|
+
const html = parse(bladeText);
|
|
81
|
+
|
|
82
|
+
return html.querySelectorAll('style[type][data-macro],script[type][data-macro]')
|
|
83
|
+
.filter(
|
|
84
|
+
(el) => ['text/scss', 'text/css'].includes(el.getAttribute('type') || '')
|
|
85
|
+
)
|
|
86
|
+
.map((el) => {
|
|
87
|
+
const scope = el.getAttribute('data-scope');
|
|
88
|
+
|
|
89
|
+
if (scope) {
|
|
90
|
+
return `${scope} {
|
|
91
|
+
${el.innerHTML}
|
|
92
|
+
}`;
|
|
93
|
+
} else {
|
|
94
|
+
return el.innerHTML;
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
})
|
|
98
|
+
.filter((c) => c.length > 0)
|
|
99
|
+
.flat();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
preview(): ProcessorPreview[] {
|
|
103
|
+
return [];
|
|
104
|
+
}
|
|
105
|
+
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import { callbackAfterBuild, copyGlob, symlink } from '@windwalker-io/fusion-next';
|
|
2
|
+
import fs from 'fs-extra';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { loadJson } from '../../utilities';
|
|
5
|
+
|
|
6
|
+
export function installVendors(
|
|
7
|
+
npmVendors: string[] = [],
|
|
8
|
+
to: string = 'www/assets/vendor',
|
|
9
|
+
) {
|
|
10
|
+
return callbackAfterBuild(() => findAndInstall(npmVendors, to));
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
enum InstallAction {
|
|
14
|
+
LINK = 'Link',
|
|
15
|
+
COPY = 'Copy',
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export async function findAndInstall(npmVendors: string[] = [], to = 'www/assets/vendor') {
|
|
19
|
+
const root = to;
|
|
20
|
+
let vendors = npmVendors;
|
|
21
|
+
const action = process.env.INSTALL_VENDOR === 'hard' ? InstallAction.COPY : InstallAction.LINK;
|
|
22
|
+
|
|
23
|
+
console.log("");
|
|
24
|
+
|
|
25
|
+
if (!fs.existsSync(root)) {
|
|
26
|
+
fs.mkdirSync(root);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const dirs = fs.readdirSync(root, { withFileTypes: true })
|
|
30
|
+
.filter(d => d.isDirectory())
|
|
31
|
+
.map(dir => path.join(root, dir.name));
|
|
32
|
+
|
|
33
|
+
dirs.unshift(root);
|
|
34
|
+
|
|
35
|
+
dirs.forEach((dir) => {
|
|
36
|
+
deleteExists(dir);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
const composerJsons = getInstalledComposerVendors()
|
|
40
|
+
.map((cv) => `vendor/${cv}/composer.json`)
|
|
41
|
+
.map((file) => loadJson(file))
|
|
42
|
+
.filter((composerJson) => composerJson?.extra?.windwalker != null);
|
|
43
|
+
|
|
44
|
+
// Install npm vendors
|
|
45
|
+
vendors = findNpmVendors(composerJsons).concat(vendors);
|
|
46
|
+
vendors = [...new Set(vendors)];
|
|
47
|
+
|
|
48
|
+
for (const vendor of vendors) {
|
|
49
|
+
const source = `node_modules/${vendor}/`;
|
|
50
|
+
if (fs.existsSync(source)) {
|
|
51
|
+
console.log(`[${action} NPM] node_modules/${vendor}/ => ${root}/${vendor}/`);
|
|
52
|
+
doInstall(source, `${root}/${vendor}/`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Install composer packages assets
|
|
57
|
+
for (const composerJson of composerJsons) {
|
|
58
|
+
const vendorName = composerJson.name;
|
|
59
|
+
|
|
60
|
+
let assets = composerJson?.extra?.windwalker?.assets?.link;
|
|
61
|
+
|
|
62
|
+
if (!assets) {
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (!assets.endsWith('/')) {
|
|
67
|
+
assets += '/';
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (fs.existsSync(`vendor/${vendorName}/${assets}`)) {
|
|
71
|
+
console.log(`[${action} Composer] vendor/${vendorName}/${assets} => ${root}/${vendorName}/`);
|
|
72
|
+
doInstall(`vendor/${vendorName}/${assets}`, `${root}/${vendorName}/`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Install legacy packages assets
|
|
77
|
+
// legacyComposerVendors.forEach((vendorName) => {
|
|
78
|
+
// console.log(vendorName, fs.existsSync(`vendor/${vendorName}/assets`));
|
|
79
|
+
// if (fs.existsSync(`vendor/${vendorName}/assets`)) {
|
|
80
|
+
// console.log(`[${action} Composer] vendor/${vendorName}/assets/ => ${root}/${vendorName}/`);
|
|
81
|
+
// doInstall(`vendor/${vendorName}/assets/`, `${root}/${vendorName}/`);
|
|
82
|
+
// }
|
|
83
|
+
// });
|
|
84
|
+
|
|
85
|
+
// Install local saved vendors
|
|
86
|
+
const staticVendorDir = 'resources/assets/vendor/';
|
|
87
|
+
|
|
88
|
+
if (fs.existsSync(staticVendorDir)) {
|
|
89
|
+
const staticVendors = fs.readdirSync(staticVendorDir);
|
|
90
|
+
|
|
91
|
+
for (const staticVendor of staticVendors) {
|
|
92
|
+
if (staticVendor.startsWith('@')) {
|
|
93
|
+
const subVendors = fs.readdirSync(staticVendorDir + staticVendor);
|
|
94
|
+
|
|
95
|
+
for (const subVendor of subVendors) {
|
|
96
|
+
const subVendorName = staticVendor + '/' + subVendor;
|
|
97
|
+
const source = staticVendorDir + subVendorName + '/';
|
|
98
|
+
|
|
99
|
+
if (fs.existsSync(source)) {
|
|
100
|
+
console.log(`[${action} Local] resources/assets/vendor/${subVendorName}/ => ${root}/${subVendorName}/`);
|
|
101
|
+
doInstall(source, `${root}/${subVendorName}/`);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
} else {
|
|
105
|
+
let source = staticVendorDir + staticVendor;
|
|
106
|
+
|
|
107
|
+
if (fs.existsSync(source)) {
|
|
108
|
+
console.log(`[${action} Local] resources/assets/vendor/${staticVendor}/ => ${root}/${staticVendor}/`);
|
|
109
|
+
doInstall(source, `${root}/${staticVendor}/`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async function doInstall(source: string, dest: string) {
|
|
117
|
+
if (process.env.INSTALL_VENDOR === 'hard') {
|
|
118
|
+
await copyGlob(source + '/**/*', dest);
|
|
119
|
+
} else {
|
|
120
|
+
await symlink(source, dest);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function findNpmVendors(composerJsons: any[] = []) {
|
|
125
|
+
const pkg = path.resolve(process.cwd(), 'package.json');
|
|
126
|
+
const pkgJson = loadJson(pkg);
|
|
127
|
+
|
|
128
|
+
let vendors = Object.keys(pkgJson.devDependencies || {})
|
|
129
|
+
.concat(Object.keys(pkgJson.dependencies || {}))
|
|
130
|
+
.map(id => `node_modules/${id}/package.json`)
|
|
131
|
+
.map((file) => loadJson(file))
|
|
132
|
+
.filter(pkgJson => pkgJson?.windwalker != null)
|
|
133
|
+
.map(pkgJson => pkgJson?.windwalker.vendors || [])
|
|
134
|
+
.flat();
|
|
135
|
+
|
|
136
|
+
const vendorsFromComposer = composerJsons
|
|
137
|
+
.map((composerJson) => {
|
|
138
|
+
return [
|
|
139
|
+
...composerJson?.extra?.windwalker?.asset_vendors || [],
|
|
140
|
+
...composerJson?.extra?.windwalker?.assets?.exposes || [],
|
|
141
|
+
...Object.keys(composerJson?.extra?.windwalker?.assets?.vendors || {})
|
|
142
|
+
];
|
|
143
|
+
})
|
|
144
|
+
.flat();
|
|
145
|
+
|
|
146
|
+
return [...new Set(vendors.concat(vendorsFromComposer))];
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function getInstalledComposerVendors() {
|
|
150
|
+
const composerFile = path.resolve(process.cwd(), 'composer.json');
|
|
151
|
+
const composerJson = loadJson(composerFile);
|
|
152
|
+
|
|
153
|
+
return [
|
|
154
|
+
...new Set(
|
|
155
|
+
Object.keys(composerJson['require'] || {})
|
|
156
|
+
.concat(Object.keys(composerJson['require-dev'] || {}))
|
|
157
|
+
)
|
|
158
|
+
];
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function deleteExists(dir: string) {
|
|
162
|
+
if (!fs.existsSync(dir)) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const subDirs = fs.readdirSync(dir, { withFileTypes: true });
|
|
167
|
+
|
|
168
|
+
for (const subDir of subDirs) {
|
|
169
|
+
if (subDir.isSymbolicLink() || subDir.isFile()) {
|
|
170
|
+
fs.unlinkSync(path.join(dir, subDir.name));
|
|
171
|
+
} else if (subDir.isDirectory()) {
|
|
172
|
+
deleteExists(path.join(dir, subDir.name));
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
fs.rmdirSync(dir);
|
|
177
|
+
}
|
|
178
|
+
|
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
import { type FindFileResult, findFilesFromGlobArray, stripUrlQuery } from '@/next';
|
|
2
|
+
import {
|
|
3
|
+
type BuildTask,
|
|
4
|
+
type ConfigBuilder,
|
|
5
|
+
type MaybePromise,
|
|
6
|
+
type ProcessorInterface,
|
|
7
|
+
type ProcessorPreview,
|
|
8
|
+
js,
|
|
9
|
+
shortHash,
|
|
10
|
+
plugin as addPlugin,
|
|
11
|
+
} from '@windwalker-io/fusion-next';
|
|
12
|
+
import { WatchTask } from '@windwalker-io/fusion-next/src/types';
|
|
13
|
+
import fs from 'fs-extra';
|
|
14
|
+
import crypto from 'node:crypto';
|
|
15
|
+
import { parse } from 'node-html-parser';
|
|
16
|
+
import { normalize, resolve } from 'node:path';
|
|
17
|
+
|
|
18
|
+
export interface JsModulizeOptions {
|
|
19
|
+
tmpPath?: string;
|
|
20
|
+
cleanTmp?: boolean;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function jsModulize(entry: string, dest: string, options: JsModulizeOptions = {}) {
|
|
24
|
+
return new JsModulizeProcessor(js(entry, dest), options);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export class JsModulizeProcessor implements ProcessorInterface {
|
|
28
|
+
protected scriptPatterns: string[] = [];
|
|
29
|
+
protected bladePatterns: string[] = [];
|
|
30
|
+
protected stagePrefix: string = '';
|
|
31
|
+
|
|
32
|
+
constructor(protected processor: ReturnType<typeof js>, protected options: JsModulizeOptions = {}) {
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
config(taskName: string, builder: ConfigBuilder) {
|
|
36
|
+
const tasks = this.processor.config(taskName, builder) as BuildTask[];
|
|
37
|
+
const task = tasks[0];
|
|
38
|
+
const inputFile = resolve(task.input);
|
|
39
|
+
const tmpPath = this.options.tmpPath ?? resolve('./tmp/fusion/jsmodules/').replace(/\\/g, '/');
|
|
40
|
+
const clean = this.options.cleanTmp ?? true;
|
|
41
|
+
|
|
42
|
+
// const appFileName = 'js/' + this.stagePrefix + '/app.js';
|
|
43
|
+
// const appSrcFileName = 'resources/assets/src/' + this.stagePrefix + '/app.js';
|
|
44
|
+
// const task = builder.addTask(appFileName);
|
|
45
|
+
|
|
46
|
+
if (clean) {
|
|
47
|
+
builder.postBuildCallbacks.push((options, bundle) => {
|
|
48
|
+
fs.removeSync(tmpPath);
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// builder.merge({
|
|
53
|
+
// resolve: {
|
|
54
|
+
// alias: {
|
|
55
|
+
// '@main': task.input
|
|
56
|
+
// }
|
|
57
|
+
// }
|
|
58
|
+
// });
|
|
59
|
+
|
|
60
|
+
// builder.entryFileNamesCallbacks.push((chunkInfo) => {
|
|
61
|
+
// if (chunkInfo.facadeModuleId === appSrcFileName) {
|
|
62
|
+
// return appFileName;
|
|
63
|
+
// }
|
|
64
|
+
// });
|
|
65
|
+
//
|
|
66
|
+
// builder.resolveIdCallbacks.push((id) => {
|
|
67
|
+
// if (id === task.input) {
|
|
68
|
+
// return appSrcFileName;
|
|
69
|
+
// }
|
|
70
|
+
// });
|
|
71
|
+
|
|
72
|
+
this.ignoreMainImport(task);
|
|
73
|
+
|
|
74
|
+
builder.resolveIdCallbacks.push((id) => {
|
|
75
|
+
if (id === '@main') {
|
|
76
|
+
return { id, external: true };
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
const scriptFiles = findFilesFromGlobArray(this.scriptPatterns);
|
|
81
|
+
const bladeFiles = parseScriptsFromBlades(this.bladePatterns);
|
|
82
|
+
|
|
83
|
+
// Watches
|
|
84
|
+
// for (const bladeFile of bladeFiles) {
|
|
85
|
+
// builder.watches.push({
|
|
86
|
+
// file: resolve(bladeFile.file.fullpath),
|
|
87
|
+
// moduleFile: inputFile,
|
|
88
|
+
// updateType: 'full-reload',
|
|
89
|
+
// } satisfies WatchTask);
|
|
90
|
+
// }
|
|
91
|
+
|
|
92
|
+
builder.loadCallbacks.push((src, options) => {
|
|
93
|
+
const srcFile = stripUrlQuery(src);
|
|
94
|
+
|
|
95
|
+
// if (src === appSrcFileName) {
|
|
96
|
+
if (normalize(srcFile) === inputFile) {
|
|
97
|
+
let listJS = "{\n";
|
|
98
|
+
|
|
99
|
+
// Merge standalone ts files
|
|
100
|
+
for (const scriptFile of scriptFiles) {
|
|
101
|
+
let fullpath = scriptFile.fullpath;
|
|
102
|
+
|
|
103
|
+
if (fullpath.endsWith('.d.ts')) {
|
|
104
|
+
continue;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
let key = scriptFile.relativePath.replace(/assets\//, '').toLowerCase();
|
|
108
|
+
fullpath = resolve(fullpath).replace(/\\/g, '/');
|
|
109
|
+
|
|
110
|
+
key = key.substring(0, key.lastIndexOf('.'));
|
|
111
|
+
|
|
112
|
+
if (this.stagePrefix) {
|
|
113
|
+
key = this.stagePrefix + '/' + key;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// md5
|
|
117
|
+
key = 'view:' + crypto.createHash('md5').update(key).digest('hex');
|
|
118
|
+
|
|
119
|
+
listJS += `'${key}': () => import('${fullpath}'),\n`;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Parse from blades
|
|
123
|
+
const listens: string[] = [];
|
|
124
|
+
|
|
125
|
+
fs.ensureDirSync(tmpPath);
|
|
126
|
+
|
|
127
|
+
for (const result of bladeFiles) {
|
|
128
|
+
let key = result.as;
|
|
129
|
+
const tmpFile = tmpPath + '/' + result.path.replace(/\\|\//g, '_') + '-' + shortHash(result.code) + '.ts';
|
|
130
|
+
|
|
131
|
+
if (!fs.existsSync(tmpFile) || fs.readFileSync(tmpFile, 'utf8') !== result.code) {
|
|
132
|
+
fs.writeFileSync(tmpFile, result.code);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// if (this.stagePrefix) {
|
|
136
|
+
// key = this.stagePrefix + '/' + key;
|
|
137
|
+
// }
|
|
138
|
+
|
|
139
|
+
listJS += `'inline:${key}': () => import('${tmpFile}'),\n`;
|
|
140
|
+
|
|
141
|
+
const fullpath = resolve(result.file.fullpath).replace(/\\/g, '/');
|
|
142
|
+
|
|
143
|
+
if (!listens.includes(fullpath)) {
|
|
144
|
+
listens.push(fullpath);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
listJS += "}";
|
|
149
|
+
|
|
150
|
+
// const loaderPath = resolve('./vendor/windwalker/core/assets/core/src/next/app.ts')
|
|
151
|
+
// .replace(/\\/g, '/');
|
|
152
|
+
//
|
|
153
|
+
// const ts = `
|
|
154
|
+
// import { App } from '${loaderPath}';
|
|
155
|
+
//
|
|
156
|
+
// const app = new App();
|
|
157
|
+
// app.registerRoutes(${listJS});
|
|
158
|
+
//
|
|
159
|
+
// export default app;
|
|
160
|
+
// `;
|
|
161
|
+
|
|
162
|
+
// Listen extra files
|
|
163
|
+
builder.watches.push(...listens);
|
|
164
|
+
|
|
165
|
+
let { code, comments } = stripComments(fs.readFileSync(srcFile, 'utf-8'));
|
|
166
|
+
|
|
167
|
+
// Replace `defineJsModules(...)`
|
|
168
|
+
code = code.replace(/defineJsModules\((.*?)\)/g, listJS);
|
|
169
|
+
|
|
170
|
+
return restoreComments(code, comments);
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
return undefined;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* @see https://github.com/vitejs/vite/issues/6393#issuecomment-1006819717
|
|
179
|
+
* @see https://stackoverflow.com/questions/76259677/vite-dev-server-throws-error-when-resolving-external-path-from-importmap
|
|
180
|
+
*/
|
|
181
|
+
private ignoreMainImport(task: BuildTask) {
|
|
182
|
+
const VALID_ID_PREFIX = `/@id/`;
|
|
183
|
+
const importKeys = ['@main'];
|
|
184
|
+
const reg = new RegExp(
|
|
185
|
+
`${VALID_ID_PREFIX}(${importKeys.join("|")})`,
|
|
186
|
+
"g"
|
|
187
|
+
);
|
|
188
|
+
|
|
189
|
+
addPlugin({
|
|
190
|
+
name: 'keep-main-external-' + task.id,
|
|
191
|
+
transform(code) {
|
|
192
|
+
return reg.test(code) ? code.replace(reg, (m, s1) => s1) : code;
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
preview(): MaybePromise<ProcessorPreview[]> {
|
|
198
|
+
return [];
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
mergeScripts(...patterns: (string | string[])[]) {
|
|
202
|
+
this.scriptPatterns = this.scriptPatterns.concat(patterns.flat());
|
|
203
|
+
|
|
204
|
+
return this;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
parseBlades(...bladePatterns: (string[] | string)[]) {
|
|
208
|
+
this.bladePatterns = this.bladePatterns.concat(bladePatterns.flat());
|
|
209
|
+
|
|
210
|
+
return this;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
stage(stage: string) {
|
|
214
|
+
this.stagePrefix = stage;
|
|
215
|
+
|
|
216
|
+
return this;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
interface ScriptResult {
|
|
221
|
+
as: string;
|
|
222
|
+
file: FindFileResult;
|
|
223
|
+
path: string;
|
|
224
|
+
code: string;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// function parseScriptsFromBlade(file: string, service: Record<string, ParsedModule>): string[] {
|
|
228
|
+
// const bladeText = fs.readFileSync(file, 'utf8');
|
|
229
|
+
//
|
|
230
|
+
// const html = parse(bladeText);
|
|
231
|
+
//
|
|
232
|
+
// return html.querySelectorAll('script[lang]')
|
|
233
|
+
// .filter(
|
|
234
|
+
// (el) => ['ts', 'typescript'].includes(el.getAttribute('lang') || '')
|
|
235
|
+
// )
|
|
236
|
+
// .map((el) => el.innerHTML)
|
|
237
|
+
// .filter((c) => c.trim() !== '');
|
|
238
|
+
// }
|
|
239
|
+
|
|
240
|
+
function parseScriptsFromBlades(patterns: string | string[]): ScriptResult[] {
|
|
241
|
+
let files = findFilesFromGlobArray(Array.isArray(patterns) ? patterns : [patterns]);
|
|
242
|
+
|
|
243
|
+
return files.map((file) => {
|
|
244
|
+
const bladeText = fs.readFileSync(file.fullpath, 'utf8');
|
|
245
|
+
|
|
246
|
+
const html = parse(bladeText);
|
|
247
|
+
// const key = file.relativePath.replace(/.blade.php$/, '').toLowerCase();
|
|
248
|
+
|
|
249
|
+
return html.querySelectorAll('script[lang][data-macro]')
|
|
250
|
+
.filter(
|
|
251
|
+
(el) => ['ts', 'typescript'].includes(el.getAttribute('lang') || '')
|
|
252
|
+
)
|
|
253
|
+
.map((el) => ({
|
|
254
|
+
as: el.getAttribute('data-macro') || '',
|
|
255
|
+
file: file,
|
|
256
|
+
path: file.relativePath.replace(/.blade.php$/, ''),
|
|
257
|
+
code: el.innerHTML
|
|
258
|
+
}))
|
|
259
|
+
.filter((c) => c.code.trim() !== '');
|
|
260
|
+
})
|
|
261
|
+
.flat();
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
type CommentPlaceholder = { key: string; value: string; };
|
|
265
|
+
|
|
266
|
+
function stripComments(code: string): { code: string; comments: CommentPlaceholder[] } {
|
|
267
|
+
const comments: CommentPlaceholder[] = [];
|
|
268
|
+
let i = 0;
|
|
269
|
+
|
|
270
|
+
code = code
|
|
271
|
+
// Multi-line /* */
|
|
272
|
+
.replace(/\/\*[\s\S]*?\*\//g, match => {
|
|
273
|
+
const key = `__COMMENT_BLOCK_${i}__`;
|
|
274
|
+
comments.push({ key, value: match });
|
|
275
|
+
i++;
|
|
276
|
+
return key;
|
|
277
|
+
})
|
|
278
|
+
// Single-line //
|
|
279
|
+
.replace(/\/\/.*$/gm, match => {
|
|
280
|
+
const key = `__COMMENT_LINE_${i}__`;
|
|
281
|
+
comments.push({ key, value: match });
|
|
282
|
+
i++;
|
|
283
|
+
return key;
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
return { code, comments };
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function restoreComments(code: string, comments: CommentPlaceholder[]): string {
|
|
290
|
+
for (const { key, value } of comments) {
|
|
291
|
+
const re = new RegExp(key, 'g');
|
|
292
|
+
code = code.replace(re, value);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
return code;
|
|
296
|
+
}
|