@serenity-is/tsbuild 9.1.0 → 10.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.d.ts +69 -34
- package/dist/index.js +285 -169
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,65 +1,100 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import esbuild from "esbuild";
|
|
2
|
+
import { type GlobOptions } from "glob";
|
|
3
|
+
export declare const defaultEntryPointGlobs: string[];
|
|
4
|
+
/**
|
|
5
|
+
* Default mapping for importing modules as globals. corelib, domwise, extensions, pro.extensions, sleekgrid
|
|
6
|
+
* are all mapped to Serenity global namespace */
|
|
7
|
+
export declare const importAsGlobalsMapping: Record<string, string>;
|
|
8
|
+
export declare function safeGlobSync(globs: string[], options?: Omit<GlobOptions, "ignore">): string[];
|
|
9
|
+
/** Default esbuild options used by TSBuild */
|
|
10
|
+
export declare const tsbuildDefaults: Partial<import("esbuild").BuildOptions>;
|
|
11
|
+
export interface TSBuildOptions extends Partial<import("esbuild").BuildOptions> {
|
|
12
|
+
/** Enable building of global iife bundles from Modules/Common/esm/bundles/*-bundle(.css|.ts) files to wwwroot/esm/bundles/. Default is false.
|
|
13
|
+
* If set to an object, uses the passed options for building global bundles. */
|
|
14
|
+
buildGlobalBundles?: boolean | TSBuildOptions;
|
|
5
15
|
/** Enable bundling of dependencies, default is true */
|
|
6
16
|
bundle?: boolean;
|
|
7
|
-
|
|
8
17
|
/** Chunk names to generate when code splitting is enabled. Default is '_chunks/[name]-[hash]' */
|
|
9
|
-
chunkNames?: string
|
|
10
|
-
|
|
18
|
+
chunkNames?: string;
|
|
11
19
|
/** True to enable the clean plugin. Default is true if splitting is true. */
|
|
12
|
-
clean?: boolean;
|
|
13
|
-
|
|
20
|
+
clean?: boolean | CleanPluginOptions;
|
|
21
|
+
/** Options for compressing output files. If specified, enables compression. Currently only available when writeIfChanged plugin is enabled */
|
|
22
|
+
compress?: CompressOptions;
|
|
14
23
|
/**
|
|
15
|
-
* Determines the set of entry points that should be passed to the esbuild.
|
|
24
|
+
* Determines the set of entry points that should be passed to the esbuild.
|
|
16
25
|
* Only use to specify full paths of entry points manually if you calculated them yourself.
|
|
17
26
|
* Prefer specifying entry point globs in sergen.json under TSBuild:EntryPoints which supports
|
|
18
27
|
* globs and defaults to ['Modules/** /*Page.ts', 'Modules/** /*Page.tsx', 'Modules/** /ScriptInit.ts'] */
|
|
19
28
|
entryPoints?: string[];
|
|
20
|
-
|
|
21
29
|
/**
|
|
22
|
-
* A set of mappings to pass to the importAsGlobalsPlugin. If this is undefined or any object and the plugins
|
|
30
|
+
* A set of mappings to pass to the importAsGlobalsPlugin. If this is undefined or any object and the plugins
|
|
23
31
|
* is not specified, importAsGlobals plugin is enabled */
|
|
24
|
-
importAsGlobals?: Record<string, string
|
|
25
|
-
|
|
32
|
+
importAsGlobals?: Record<string, string> | null;
|
|
26
33
|
/**
|
|
27
34
|
* True to enable metafile generation by esbuild. Default is true.
|
|
28
35
|
* If this is false, clean plugin won't work properly.
|
|
29
36
|
*/
|
|
30
37
|
metafile?: boolean;
|
|
31
|
-
|
|
32
38
|
/** True to enable minification. Default is true. */
|
|
33
39
|
minify?: boolean;
|
|
34
|
-
|
|
35
40
|
/** False to not call npmCopy automatically for entries in appsettings.bundles.json that start with `~/npm/`. Default is true.*/
|
|
36
41
|
npmCopy?: boolean;
|
|
37
|
-
|
|
38
42
|
/** Base directory for calculating output file locations in output directory. Default is "./" */
|
|
39
43
|
outbase?: string;
|
|
40
|
-
|
|
41
44
|
/** Base output directory. Default is wwwroot/esm */
|
|
42
|
-
outdir?:
|
|
43
|
-
|
|
45
|
+
outdir?: string;
|
|
44
46
|
/** True to enable code splitting. Default is true unless --nosplit is passed in process arguments. */
|
|
45
47
|
splitting?: boolean;
|
|
46
|
-
|
|
47
|
-
/** Set of plugins for esbuild */
|
|
48
|
-
plugins?: any[];
|
|
49
|
-
|
|
50
48
|
/** Should source maps be generated. Default is true. */
|
|
51
49
|
sourcemap?: boolean;
|
|
52
|
-
|
|
53
|
-
/* Javascript target for output files. Default is es6. */
|
|
54
50
|
target?: string;
|
|
55
|
-
|
|
56
51
|
/** True to watch, default is calculated from process args and true if it contains --watch */
|
|
57
52
|
watch?: boolean;
|
|
53
|
+
/** Write output files only if contents have changed. Default is true. */
|
|
54
|
+
writeIfChanged?: boolean;
|
|
58
55
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
export const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
export
|
|
56
|
+
/** Processes passed TSBuildOptions options and converts it to options suitable for esbuild */
|
|
57
|
+
export declare const esbuildOptions: (opt: TSBuildOptions) => import("esbuild").BuildOptions;
|
|
58
|
+
/** Default options for global iife bundle builds which is used when buildGlobalBundles is true or is an object */
|
|
59
|
+
export declare const tsbuildGlobalBundleDefaults: Partial<TSBuildOptions>;
|
|
60
|
+
/** Calls esbuild with passed options. By default, this is used to generate files under wwwroot/esm/ from entry points under Modules/
|
|
61
|
+
* but this can be changed by passing outdir and outbase, and other options. */
|
|
62
|
+
export declare const build: (opt: TSBuildOptions) => Promise<void>;
|
|
63
|
+
/** Plugin for importing modules as globals */
|
|
64
|
+
export declare function importAsGlobalsPlugin(mapping: Record<string, string>): {
|
|
65
|
+
name: string;
|
|
66
|
+
setup(build: esbuild.PluginBuild): void;
|
|
67
|
+
};
|
|
68
|
+
/** Default options for cleanPlugin */
|
|
69
|
+
export declare const cleanPluginDefaults: {
|
|
70
|
+
globs: string[];
|
|
71
|
+
logDeletedFiles: boolean;
|
|
72
|
+
};
|
|
73
|
+
/** Options for cleanPlugin */
|
|
74
|
+
export interface CleanPluginOptions {
|
|
75
|
+
/** Glob patterns to include for cleaning. Default is ['** /*.js', '** /*.js.map', '** /*.css', '** /*.css.map'] */
|
|
76
|
+
globs?: string[];
|
|
77
|
+
/** Whether to log deleted files to console. Default is true */
|
|
78
|
+
logDeletedFiles?: boolean;
|
|
79
|
+
}
|
|
80
|
+
/** Plugin for cleaning output directory based on passed globs */
|
|
81
|
+
export declare function cleanPlugin(opt: CleanPluginOptions): {
|
|
82
|
+
name: string;
|
|
83
|
+
setup(build: esbuild.PluginBuild): void;
|
|
84
|
+
};
|
|
85
|
+
export interface CompressOptions {
|
|
86
|
+
brotli?: boolean | {
|
|
87
|
+
quality?: number;
|
|
88
|
+
};
|
|
89
|
+
gzip?: boolean | {
|
|
90
|
+
level?: number;
|
|
91
|
+
};
|
|
92
|
+
extensions?: string[];
|
|
93
|
+
}
|
|
94
|
+
/** Plugin for writing files only if changed */
|
|
95
|
+
export declare function writeIfChanged(opt?: CompressOptions): {
|
|
96
|
+
name: string;
|
|
97
|
+
setup(build: esbuild.PluginBuild): void;
|
|
98
|
+
};
|
|
99
|
+
/** Copies files from node_modules to outdir (wwwroot/npm by default). Paths are relative to node_modules. */
|
|
100
|
+
export declare function npmCopy(paths: string[]): void;
|
package/dist/index.js
CHANGED
|
@@ -1,217 +1,301 @@
|
|
|
1
1
|
import esbuild from "esbuild";
|
|
2
|
-
import {
|
|
3
|
-
import { dirname, join, relative, resolve } from "path";
|
|
2
|
+
import { createReadStream, createWriteStream, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "fs";
|
|
4
3
|
import { globSync } from "glob";
|
|
4
|
+
import pipe from "node:stream/promises";
|
|
5
|
+
import { constants, createBrotliCompress, createGzip } from "node:zlib";
|
|
6
|
+
import { dirname, isAbsolute, join, resolve, sep } from "path";
|
|
5
7
|
|
|
6
|
-
export const defaultEntryPointGlobs = [
|
|
8
|
+
export const defaultEntryPointGlobs = [
|
|
9
|
+
"Modules/**/*Page.ts",
|
|
10
|
+
"Modules/**/*Page.tsx",
|
|
11
|
+
"Modules/**/ScriptInit.ts",
|
|
12
|
+
"Modules/**/*.mts"
|
|
13
|
+
];
|
|
7
14
|
|
|
8
15
|
export const importAsGlobalsMapping = {
|
|
9
|
-
"@serenity-is/base": "Serenity",
|
|
10
16
|
"@serenity-is/corelib": "Serenity",
|
|
11
|
-
"@serenity-is/
|
|
12
|
-
"@serenity-is/
|
|
13
|
-
"@serenity-is/
|
|
14
|
-
"@serenity-is/extensions": "Serenity
|
|
15
|
-
"@serenity-is/
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export const esbuildOptions = (opt) => {
|
|
17
|
+
"@serenity-is/domwise": "Serenity",
|
|
18
|
+
"@serenity-is/domwise/jsx-runtime": "Serenity",
|
|
19
|
+
"@serenity-is/extensions": "Serenity",
|
|
20
|
+
"@serenity-is/pro.extensions": "Serenity",
|
|
21
|
+
"@serenity-is/sleekgrid": "Serenity"
|
|
22
|
+
};
|
|
19
23
|
|
|
20
|
-
|
|
24
|
+
export function safeGlobSync(globs, options = {}) {
|
|
25
|
+
const root = options.root || process.cwd();
|
|
26
|
+
const normalizePattern = (pattern) => {
|
|
27
|
+
let normalized = pattern.replace(/\\/g, "/");
|
|
28
|
+
if (normalized.startsWith("/")) {
|
|
29
|
+
normalized = "." + normalized;
|
|
30
|
+
} else if (!normalized.includes("/")) {
|
|
31
|
+
normalized = "**/" + normalized;
|
|
32
|
+
}
|
|
33
|
+
if (isAbsolute(normalized) || normalized.includes("..") || /^[a-zA-Z]:/.test(normalized)) {
|
|
34
|
+
throw new Error(`Invalid pattern: ${pattern}`);
|
|
35
|
+
}
|
|
36
|
+
return normalized;
|
|
37
|
+
};
|
|
38
|
+
globs = globs.map((x) => x?.trim()).filter((x) => x.length > 0);
|
|
39
|
+
const includes = globs.filter((x) => !x.startsWith("!")).map(normalizePattern);
|
|
40
|
+
const excludes = globs.filter((x) => x.startsWith("!")).map((x) => normalizePattern(x.substring(1)));
|
|
41
|
+
const results = globSync(includes, {
|
|
42
|
+
nodir: true,
|
|
43
|
+
ignore: excludes,
|
|
44
|
+
matchBase: true,
|
|
45
|
+
cwd: root,
|
|
46
|
+
...options
|
|
47
|
+
});
|
|
48
|
+
const resolvedRoot = resolve(root);
|
|
49
|
+
return results.filter((file) => {
|
|
50
|
+
const fullPath = resolve(root, file);
|
|
51
|
+
return fullPath.startsWith(resolvedRoot + sep) || fullPath === resolvedRoot;
|
|
52
|
+
});
|
|
53
|
+
}
|
|
21
54
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
55
|
+
export const tsbuildDefaults = {
|
|
56
|
+
assetNames: "assets/[name]-[hash]",
|
|
57
|
+
bundle: true,
|
|
58
|
+
chunkNames: "_chunks/[name]-[hash]",
|
|
59
|
+
color: true,
|
|
60
|
+
format: "esm",
|
|
61
|
+
jsxSideEffects: true,
|
|
62
|
+
keepNames: true,
|
|
63
|
+
loader: {
|
|
64
|
+
".woff2": "file",
|
|
65
|
+
".woff": "file",
|
|
66
|
+
".ttf": "file",
|
|
67
|
+
".eot": "file",
|
|
68
|
+
".svg": "file",
|
|
69
|
+
".png": "file",
|
|
70
|
+
".jpg": "file",
|
|
71
|
+
".jpeg": "file",
|
|
72
|
+
".gif": "file",
|
|
73
|
+
".webp": "file"
|
|
74
|
+
},
|
|
75
|
+
logLevel: "info",
|
|
76
|
+
metafile: true,
|
|
77
|
+
minify: true,
|
|
78
|
+
outbase: "./",
|
|
79
|
+
outdir: "wwwroot/esm",
|
|
80
|
+
sourcemap: true,
|
|
81
|
+
target: "es2017"
|
|
82
|
+
};
|
|
27
83
|
|
|
28
|
-
|
|
29
|
-
if (opt.
|
|
30
|
-
|
|
31
|
-
delete opt.entryPointRoots;
|
|
84
|
+
function isSplittingEnabled(opt) {
|
|
85
|
+
if (opt.splitting !== void 0) {
|
|
86
|
+
return !!opt.splitting;
|
|
32
87
|
}
|
|
88
|
+
return (opt.format == null || opt.format === "esm") && !globalThis.process.argv.slice(2).some((x) => x == "--nosplit");
|
|
89
|
+
}
|
|
33
90
|
|
|
91
|
+
function cleanPluginOptions(opt) {
|
|
92
|
+
if (opt.plugins === void 0)
|
|
93
|
+
return null;
|
|
94
|
+
if (opt.clean === void 0 && isSplittingEnabled(opt) || opt.clean)
|
|
95
|
+
return opt.clean === true ? {} : opt.clean ?? {};
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
34
98
|
|
|
35
|
-
|
|
99
|
+
export const esbuildOptions = (opt) => {
|
|
100
|
+
opt = Object.assign({}, opt);
|
|
101
|
+
if (opt.entryPointsRegex !== void 0 || opt.entryPointRoots !== void 0) {
|
|
102
|
+
throw new Error("TSBuildOptions.entryPointsRegex and entryPointRoots are deprecated, use TSBuild:EntryPoints in sergen.json.");
|
|
103
|
+
}
|
|
104
|
+
let entryPoints = opt.entryPoints;
|
|
36
105
|
if (entryPoints === void 0) {
|
|
37
106
|
let globs;
|
|
38
|
-
if (existsSync(
|
|
39
|
-
var json = readFileSync(
|
|
40
|
-
var cfg = JSON.parse(json || {});
|
|
107
|
+
if (existsSync("sergen.json")) {
|
|
108
|
+
var json = readFileSync("sergen.json", "utf8").trim();
|
|
109
|
+
var cfg = JSON.parse(json || "{}");
|
|
41
110
|
globs = cfg?.TSBuild?.EntryPoints;
|
|
42
|
-
if (globs != null && globs[0] ===
|
|
111
|
+
if (globs != null && globs[0] === "+") {
|
|
43
112
|
globs = [...defaultEntryPointGlobs, ...globs.slice(1)];
|
|
44
113
|
}
|
|
45
|
-
if (globs === void 0 &&
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
json = readFileSync(cfg.Extends, 'utf8').trim();
|
|
49
|
-
cfg = JSON.parse(json || {});
|
|
114
|
+
if (globs === void 0 && typeof cfg.Extends == "string" && existsSync(cfg.Extends)) {
|
|
115
|
+
json = readFileSync(cfg.Extends, "utf8").trim();
|
|
116
|
+
cfg = JSON.parse(json || "{}");
|
|
50
117
|
globs = cfg?.TSBuild?.EntryPoints;
|
|
51
|
-
if (globs != null && globs[0] ===
|
|
118
|
+
if (globs != null && globs[0] === "+") {
|
|
52
119
|
globs = [...defaultEntryPointGlobs, ...globs.slice(1)];
|
|
53
120
|
}
|
|
54
121
|
}
|
|
55
122
|
}
|
|
56
|
-
|
|
57
|
-
if (globs == null && !entryPointsRegEx) {
|
|
123
|
+
if (globs == null) {
|
|
58
124
|
globs = defaultEntryPointGlobs;
|
|
59
125
|
}
|
|
60
|
-
|
|
61
126
|
if (globs != null) {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
exclude.push("node_modules/**");
|
|
69
|
-
exclude.push("**/node_modules/**");
|
|
70
|
-
|
|
71
|
-
entryPoints = globSync(include, {
|
|
72
|
-
ignore: exclude,
|
|
73
|
-
nodir: true,
|
|
74
|
-
matchBase: true
|
|
75
|
-
});
|
|
76
|
-
}
|
|
77
|
-
else {
|
|
78
|
-
entryPoints = [];
|
|
79
|
-
entryPointRoots.forEach(root =>
|
|
80
|
-
scanDir(root)
|
|
81
|
-
.filter(p => p.match(entryPointsRegEx))
|
|
82
|
-
.forEach(p => entryPoints.push(root + '/' + p)));
|
|
127
|
+
globs.push("!.git/**");
|
|
128
|
+
globs.push("!App_Data/**");
|
|
129
|
+
globs.push("!bin/**");
|
|
130
|
+
globs.push("!obj/**");
|
|
131
|
+
globs.push("!node_modules/**");
|
|
132
|
+
entryPoints = safeGlobSync(globs);
|
|
83
133
|
}
|
|
84
134
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if (
|
|
88
|
-
splitting = !process.argv.slice(2).some(x => x == "--nosplit");
|
|
89
|
-
|
|
90
|
-
var plugins = opt.plugins;
|
|
91
|
-
if (plugins === undefined) {
|
|
135
|
+
const splitting = isSplittingEnabled(opt);
|
|
136
|
+
let plugins = opt.plugins;
|
|
137
|
+
if (plugins === void 0) {
|
|
92
138
|
plugins = [];
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
139
|
+
const cleanOpt = cleanPluginOptions(opt);
|
|
140
|
+
if (cleanOpt != null)
|
|
141
|
+
plugins.push(cleanPlugin(cleanOpt));
|
|
142
|
+
if (opt.importAsGlobals === void 0 || opt.importAsGlobals)
|
|
96
143
|
plugins.push(importAsGlobalsPlugin(opt.importAsGlobals ?? importAsGlobalsMapping));
|
|
97
144
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
plugins.push(writeIfChanged());
|
|
145
|
+
if (opt.write === void 0 && opt.writeIfChanged === void 0 || opt.writeIfChanged) {
|
|
146
|
+
plugins.push(writeIfChanged(opt.compress));
|
|
101
147
|
opt.write = false;
|
|
102
148
|
}
|
|
103
|
-
|
|
149
|
+
delete opt.compress;
|
|
104
150
|
delete opt.clean;
|
|
105
151
|
delete opt.importAsGlobals;
|
|
106
152
|
delete opt.writeIfChanged;
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
outdir: 'wwwroot/esm',
|
|
153
|
+
if (opt.sourceRoot === void 0) {
|
|
154
|
+
if (existsSync("package.json")) {
|
|
155
|
+
let pkgId = JSON.parse(readFileSync("package.json", "utf8").trim() || "{}").name;
|
|
156
|
+
if (pkgId.startsWith("@serenity-is/")) {
|
|
157
|
+
opt.sourceRoot = "https://packages.serenity.is/" + pkgId.substring(13) + "/Modules/";
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
opt.sourceRoot ??= "Modules";
|
|
161
|
+
}
|
|
162
|
+
return {
|
|
163
|
+
...tsbuildDefaults,
|
|
164
|
+
absWorkingDir: resolve("./"),
|
|
165
|
+
entryPoints,
|
|
121
166
|
plugins,
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
}
|
|
127
|
-
}
|
|
167
|
+
splitting,
|
|
168
|
+
// @ts-ignore
|
|
169
|
+
watch: process.argv.slice(2).some((x) => x == "--watch"),
|
|
170
|
+
...opt
|
|
171
|
+
};
|
|
172
|
+
};
|
|
173
|
+
|
|
174
|
+
export const tsbuildGlobalBundleDefaults = {
|
|
175
|
+
entryPoints: [
|
|
176
|
+
"Modules/Common/bundles/*-bundle.ts",
|
|
177
|
+
"Modules/Common/bundles/*-bundle.css",
|
|
178
|
+
"Modules/Common/bundles/*-bundle.rtl.css"
|
|
179
|
+
],
|
|
180
|
+
format: "iife",
|
|
181
|
+
importAsGlobals: null,
|
|
182
|
+
outdir: "wwwroot/esm/bundles/",
|
|
183
|
+
outbase: "Modules/Common/bundles",
|
|
184
|
+
watch: false
|
|
185
|
+
};
|
|
128
186
|
|
|
129
187
|
export const build = async (opt) => {
|
|
130
|
-
if (opt
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
188
|
+
if (opt.buildGlobalBundles) {
|
|
189
|
+
const buildGlobalBundles = {
|
|
190
|
+
...tsbuildGlobalBundleDefaults,
|
|
191
|
+
...opt.buildGlobalBundles === true ? {} : opt.buildGlobalBundles
|
|
192
|
+
};
|
|
193
|
+
delete opt.buildGlobalBundles;
|
|
194
|
+
console.log("\x1B[32mBuilding global bundles...\x1B[0m");
|
|
195
|
+
await build(buildGlobalBundles);
|
|
196
|
+
let cleanOpt = cleanPluginOptions(opt);
|
|
197
|
+
if (cleanOpt != null) {
|
|
198
|
+
opt.clean = {
|
|
199
|
+
...cleanOpt,
|
|
200
|
+
globs: [
|
|
201
|
+
"!./bundles/**",
|
|
202
|
+
...cleanOpt.globs ?? cleanPluginDefaults.globs
|
|
203
|
+
]
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if (opt?.npmCopy !== false && existsSync("appsettings.bundles.json")) {
|
|
208
|
+
const bundlesJson = readFileSync("appsettings.bundles.json", "utf8").trim();
|
|
209
|
+
const bundlesCfg = JSON.parse(bundlesJson || "{}");
|
|
134
210
|
const bundles = Object.values(bundlesCfg?.CssBundling?.Bundles || {}).concat(Object.values(bundlesCfg?.ScriptBundling?.Bundles || {}));
|
|
135
211
|
let paths = [];
|
|
136
|
-
Object.values(bundles).filter(x => x?.length).forEach(bundle => {
|
|
137
|
-
paths.push(...bundle.filter(f => f?.startsWith(
|
|
212
|
+
Object.values(bundles).filter((x) => x?.length).forEach((bundle) => {
|
|
213
|
+
paths.push(...bundle.filter((f) => f?.startsWith("~/npm/")).map((f) => f.substring(5)));
|
|
138
214
|
});
|
|
139
|
-
paths = paths.filter((v, i, a) => a.indexOf(v) === i);
|
|
215
|
+
paths = paths.filter((v, i, a) => a.indexOf(v) === i);
|
|
140
216
|
if (paths.length) {
|
|
141
217
|
npmCopy(paths);
|
|
142
|
-
}
|
|
218
|
+
}
|
|
143
219
|
}
|
|
144
|
-
|
|
145
220
|
delete opt?.npmCopy;
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
if (opt.watch) {
|
|
150
|
-
// this somehow resolves the issue that when debugging is stopped
|
|
151
|
-
// in Visual Studio, the node process stays alive
|
|
221
|
+
const esopt = esbuildOptions(opt);
|
|
222
|
+
if (esopt.watch) {
|
|
152
223
|
setInterval(() => {
|
|
153
224
|
process.stdout.write("");
|
|
154
|
-
},
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
const context = await esbuild.context(opt);
|
|
225
|
+
}, 5e3);
|
|
226
|
+
delete esopt.watch;
|
|
227
|
+
const context = await esbuild.context(esopt);
|
|
158
228
|
await context.watch();
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
await esbuild.build(opt);
|
|
229
|
+
} else {
|
|
230
|
+
delete esopt.watch;
|
|
231
|
+
await esbuild.build(esopt);
|
|
163
232
|
}
|
|
164
233
|
};
|
|
165
234
|
|
|
166
|
-
function scanDir(dir, org) {
|
|
167
|
-
return readdirSync(dir).reduce((files, file) => {
|
|
168
|
-
const absolute = join(dir, file);
|
|
169
|
-
return [...files, ...(statSync(absolute).isDirectory()
|
|
170
|
-
? scanDir(absolute, org || dir)
|
|
171
|
-
: [relative(org || dir, absolute)])]
|
|
172
|
-
}, []);
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
// https://github.com/evanw/esbuild/issues/337
|
|
176
235
|
export function importAsGlobalsPlugin(mapping) {
|
|
177
236
|
const escRe = (s) => s.replace(/[-\/\\^$*+?.()|[\]{}]/g, "\\$&");
|
|
178
|
-
const filter = new RegExp(Object.keys(mapping).map((mod) =>
|
|
179
|
-
`^${escRe(mod)}$`).join("|"));
|
|
180
|
-
|
|
237
|
+
const filter = new RegExp(Object.keys(mapping).map((mod) => `^${escRe(mod)}$`).join("|"));
|
|
181
238
|
return {
|
|
182
239
|
name: "global-imports",
|
|
183
|
-
setup(
|
|
184
|
-
|
|
240
|
+
setup(build2) {
|
|
241
|
+
build2.onResolve({ filter }, (args) => {
|
|
185
242
|
if (!mapping[args.path])
|
|
186
243
|
throw new Error("Unknown global: " + args.path);
|
|
187
244
|
return { path: mapping[args.path], namespace: "external-global" };
|
|
188
245
|
});
|
|
189
|
-
|
|
190
|
-
|
|
246
|
+
build2.onLoad(
|
|
247
|
+
{ filter: /.*/, namespace: "external-global" },
|
|
191
248
|
async (args) => {
|
|
192
249
|
return { contents: `module.exports = ${args.path};`, loader: "js" };
|
|
193
|
-
}
|
|
250
|
+
}
|
|
251
|
+
);
|
|
194
252
|
}
|
|
195
253
|
};
|
|
196
254
|
}
|
|
197
255
|
|
|
198
|
-
export
|
|
256
|
+
export const cleanPluginDefaults = {
|
|
257
|
+
globs: [
|
|
258
|
+
"*.css",
|
|
259
|
+
"*.css.map",
|
|
260
|
+
"*.js",
|
|
261
|
+
"*.js.map",
|
|
262
|
+
"*.jpg",
|
|
263
|
+
"*.png",
|
|
264
|
+
"*.gif",
|
|
265
|
+
"*.svg",
|
|
266
|
+
"*.woff",
|
|
267
|
+
"*.woff2",
|
|
268
|
+
"*.ttf",
|
|
269
|
+
"*.eot"
|
|
270
|
+
],
|
|
271
|
+
logDeletedFiles: true
|
|
272
|
+
};
|
|
273
|
+
function cleanPlugin(opt) {
|
|
274
|
+
opt = Object.assign({}, cleanPluginDefaults, opt ?? {});
|
|
199
275
|
return {
|
|
200
|
-
name:
|
|
201
|
-
setup(
|
|
202
|
-
|
|
276
|
+
name: "clean",
|
|
277
|
+
setup(build2) {
|
|
278
|
+
build2.onEnd((result) => {
|
|
203
279
|
try {
|
|
280
|
+
const outdir = build2.initialOptions.outdir;
|
|
204
281
|
const { outputs } = result.metafile ?? {};
|
|
205
|
-
if (!outputs || !existsSync(
|
|
282
|
+
if (!outputs || !existsSync(outdir))
|
|
206
283
|
return;
|
|
207
|
-
|
|
208
|
-
const
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
if (!outputFiles.has(
|
|
213
|
-
|
|
214
|
-
|
|
284
|
+
const outputFiles = new Set(Object.keys(outputs).map((x) => x.replace(/\\/g, "/")));
|
|
285
|
+
const existingFiles = safeGlobSync(opt.globs || [], {
|
|
286
|
+
cwd: outdir
|
|
287
|
+
}).map((x) => join(outdir, x).replace(/\\/g, "/"));
|
|
288
|
+
existingFiles.forEach((file) => {
|
|
289
|
+
if (!outputFiles.has(file)) {
|
|
290
|
+
if (opt.logDeletedFiles ?? true)
|
|
291
|
+
console.log(`esbuild clean: \x1B[33mdeleting extra file ${file}\x1B[0m`);
|
|
292
|
+
rmSync(file);
|
|
293
|
+
if (existsSync(file + ".gz")) {
|
|
294
|
+
rmSync(file + ".gz");
|
|
295
|
+
}
|
|
296
|
+
if (existsSync(file + ".br")) {
|
|
297
|
+
rmSync(file + ".br");
|
|
298
|
+
}
|
|
215
299
|
}
|
|
216
300
|
});
|
|
217
301
|
} catch (e) {
|
|
@@ -219,59 +303,91 @@ export function cleanPlugin() {
|
|
|
219
303
|
}
|
|
220
304
|
});
|
|
221
305
|
}
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
export function checkIfTrigger() {
|
|
226
|
-
// nop
|
|
306
|
+
};
|
|
227
307
|
}
|
|
228
308
|
|
|
229
|
-
export function writeIfChanged() {
|
|
309
|
+
export function writeIfChanged(opt) {
|
|
310
|
+
const compressExtensions = opt?.extensions ?? [".css", ".js", ".svg", ".json"];
|
|
230
311
|
return {
|
|
231
312
|
name: "write-if-changed",
|
|
232
|
-
setup(
|
|
233
|
-
|
|
234
|
-
|
|
313
|
+
setup(build2) {
|
|
314
|
+
build2.onEnd(async (result) => {
|
|
315
|
+
const start = (/* @__PURE__ */ new Date()).getTime();
|
|
316
|
+
let compressed = 0;
|
|
317
|
+
let written = 0;
|
|
318
|
+
let checkedFiles = 0;
|
|
319
|
+
let outputFiles = result.outputFiles || [];
|
|
320
|
+
for (const file of outputFiles) {
|
|
321
|
+
checkedFiles++;
|
|
322
|
+
const compressFile = async () => {
|
|
323
|
+
if ((opt?.brotli || opt?.gzip) && compressExtensions.some((ext) => file.path?.endsWith(ext))) {
|
|
324
|
+
if (opt.brotli) {
|
|
325
|
+
await pipe.pipeline(
|
|
326
|
+
createReadStream(file.path),
|
|
327
|
+
createBrotliCompress({
|
|
328
|
+
[constants.BROTLI_PARAM_QUALITY]: typeof opt.brotli === "object" && opt.brotli.quality || 4
|
|
329
|
+
}),
|
|
330
|
+
createWriteStream(`${file.path}.br`)
|
|
331
|
+
);
|
|
332
|
+
compressed++;
|
|
333
|
+
}
|
|
334
|
+
if (opt.gzip) {
|
|
335
|
+
await pipe.pipeline(
|
|
336
|
+
createReadStream(file.path),
|
|
337
|
+
createGzip({ level: typeof opt.gzip === "object" && opt.gzip.level || 7 }),
|
|
338
|
+
createWriteStream(`${file.path}.gz`)
|
|
339
|
+
);
|
|
340
|
+
compressed++;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
};
|
|
235
344
|
if (existsSync(file.path)) {
|
|
236
345
|
const old = readFileSync(file.path);
|
|
237
|
-
if (old.equals(file.contents))
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
346
|
+
if (old.equals(file.contents)) {
|
|
347
|
+
if (opt?.brotli && !existsSync(file.path + ".br") || opt?.gzip && !existsSync(file.path + ".gz")) {
|
|
348
|
+
await compressFile();
|
|
349
|
+
}
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
} else {
|
|
241
353
|
mkdirSync(dirname(file.path), { recursive: true });
|
|
242
354
|
}
|
|
243
|
-
writeFileSync(file.path, file.
|
|
244
|
-
|
|
355
|
+
writeFileSync(file.path, file.contents);
|
|
356
|
+
written++;
|
|
357
|
+
await compressFile();
|
|
358
|
+
}
|
|
359
|
+
const end = (/* @__PURE__ */ new Date()).getTime();
|
|
360
|
+
if (written > 0 || compressed > 0)
|
|
361
|
+
console.log(`esbuild write: \x1B[32mChecked ${checkedFiles} output files in ${end - start} ms, changed ${written}, compressed ${compressed}\x1B[0m`);
|
|
362
|
+
else
|
|
363
|
+
console.log(`esbuild write: \x1B[32mChecked ${checkedFiles} output files in ${end - start} ms, none changed\x1B[0m`);
|
|
364
|
+
await Promise.resolve();
|
|
245
365
|
});
|
|
246
366
|
}
|
|
247
367
|
};
|
|
248
368
|
}
|
|
249
369
|
|
|
250
370
|
export function npmCopy(paths) {
|
|
251
|
-
paths.forEach(path => {
|
|
371
|
+
paths.forEach((path) => {
|
|
252
372
|
const srcFile = join("node_modules", path);
|
|
253
373
|
const dstfile = join("wwwroot/npm", path);
|
|
254
374
|
if (!existsSync(srcFile)) {
|
|
255
375
|
console.warn(`Source file not found: ${srcFile}`);
|
|
256
376
|
return;
|
|
257
377
|
}
|
|
258
|
-
|
|
259
|
-
(function() {
|
|
378
|
+
(function () {
|
|
260
379
|
const srcContent = readFileSync(srcFile);
|
|
261
380
|
if (existsSync(dstfile)) {
|
|
262
381
|
if (readFileSync(dstfile).equals(srcContent))
|
|
263
382
|
return;
|
|
264
|
-
}
|
|
265
|
-
else {
|
|
383
|
+
} else {
|
|
266
384
|
mkdirSync(dirname(dstfile), { recursive: true });
|
|
267
385
|
}
|
|
268
386
|
console.log(`Copying ${srcFile} to ${dstfile}`);
|
|
269
387
|
writeFileSync(dstfile, srcContent);
|
|
270
388
|
})();
|
|
271
|
-
|
|
272
389
|
const js = path.endsWith(".js");
|
|
273
|
-
if (
|
|
274
|
-
(path.endsWith(".css") && !path.endsWith(".min.css"))) {
|
|
390
|
+
if (js && !path.endsWith(".min.js") || path.endsWith(".css") && !path.endsWith(".min.css")) {
|
|
275
391
|
const ext = js ? ".min.js" : ".min.css";
|
|
276
392
|
const srcMinFile = srcFile.substring(0, srcFile.length - (js ? 3 : 4)) + ext;
|
|
277
393
|
const dstMinFile = dstfile.substring(0, dstfile.length - (js ? 3 : 4)) + ext;
|
|
@@ -284,4 +400,4 @@ export function npmCopy(paths) {
|
|
|
284
400
|
}
|
|
285
401
|
}
|
|
286
402
|
});
|
|
287
|
-
}
|
|
403
|
+
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@serenity-is/tsbuild",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "10.0.3",
|
|
4
4
|
"author": "Serenity (https://serenity.is)",
|
|
5
5
|
"bugs": "https://github.com/serenity-is/serenity/issues",
|
|
6
6
|
"description": "Serenity ESBuild functions",
|
|
7
7
|
"dependencies": {
|
|
8
8
|
"esbuild": "0.27.0",
|
|
9
|
-
"glob": "
|
|
9
|
+
"glob": "13.0.0"
|
|
10
10
|
},
|
|
11
11
|
"exports": {
|
|
12
12
|
".": {
|