-
Notifications
You must be signed in to change notification settings - Fork 0
/
esbuild.config.js
192 lines (177 loc) · 6.77 KB
/
esbuild.config.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
const esbuild = require("esbuild");
const fs = require("node:fs/promises");
const pkg = require("./package.json");
const postcss = require("postcss");
const sass = require("sass");
const { transform } = require("@svgr/core");
const url = require("postcss-url");
// this function generates app props based on package.json and propSecrets.json
const appProps = require("./helpers/appProps");
if (!/.+\/.+\.js/.test(pkg.module)) throw new Error("module value is incorrect, use DIR/FILE.js like build/index.js");
const isProduction = process.env.NODE_ENV === "production";
// If the jspm server fails and we cannfot use external packages
// in our import map then IGNORE_EXTERNALS (global env variable)
// should be set to true
const IGNORE_EXTERNALS = process.env.IGNORE_EXTERNALS === "true";
// in dev environment we prefix output file with public
let outfile = `${isProduction ? "" : "public/"}${pkg.main || pkg.module}`;
// get output from outputfile
let outdir = outfile.slice(0, outfile.lastIndexOf("/"));
const args = process.argv.slice(2);
const watch = args.indexOf("--watch") >= 0;
const serve = args.indexOf("--serve") >= 0;
// helpers for console log
const green = "\x1b[32m%s\x1b[0m";
const yellow = "\x1b[33m%s\x1b[0m";
const clear = "\033c";
const build = async () => {
// delete build folder and re-create it as an empty folder
await fs.rm(outdir, { recursive: true, force: true });
await fs.mkdir(outdir, { recursive: true });
// build app
let ctx = await esbuild.context({
bundle: true,
minify: isProduction,
// target: ["es2020"],
target: ["es2020"], //["chrome64", "firefox67", "safari11.1", "edge79"],
format: "esm",
platform: "browser",
// built-in loaders: js, jsx, ts, tsx, css, json, text, base64, dataurl, file, binary
loader: { ".js": "jsx" },
sourcemap: !isProduction,
// here we exclude package from bundle which are defined in peerDependencies
// our importmap generator uses also the peerDependencies to create the importmap
// it means all packages defined in peerDependencies are in browser available via the importmap
external: isProduction && !IGNORE_EXTERNALS ? Object.keys(pkg.peerDependencies || {}) : [],
entryPoints: [pkg.source, "src/quotaPlugin.js"],
outdir,
// this step is important for performance reason.
// the main file (index.js) contains minimal code needed to
// load the app via dynamic import (splitting: true)
splitting: true,
// we suport only esm!
format: "esm",
plugins: [
// minimal plugin to log the recompiling process.
{
name: "start/end",
setup(build) {
build.onStart(() => {
console.log(clear);
console.log(yellow, "Compiling...");
});
build.onEnd(() => console.log(green, "Done!"));
},
},
// this custom plugin rewrites SVG imports to
// dataurls, paths or react components based on the
// search param and size
{
name: "svg-loader",
setup(build) {
build.onLoad(
// consider only .svg files
{ filter: /.\.(svg)$/, namespace: "file" },
async (args) => {
let contents = await fs.readFile(args.path);
// built-in loaders: js, jsx, ts, tsx, css, json, text, base64, dataurl, file, binary
let loader = "text";
if (args.suffix === "?url") {
// as URL
const maxSize = 10240; // 10Kb
// use dataurl loader for small files and file loader for big files!
loader = contents.length <= maxSize ? "dataurl" : "file";
} else {
// as react component
// use react component loader (jsx)
loader = "jsx";
contents = await transform(contents, {
plugins: ["@svgr/plugin-jsx"],
});
}
return { contents, loader };
}
);
},
},
// this custom plugin rewrites image imports to
// dataurls or urls based on the size
{
name: "image-loader",
setup(build) {
build.onLoad(
// consider only .svg files
{ filter: /.\.(png|jpg|jpeg|gif)$/, namespace: "file" },
async (args) => {
let contents = await fs.readFile(args.path);
const maxSize = 10240; // 10Kb
// built-in loaders: js, jsx, ts, tsx, css, json, text, base64, dataurl, file, binary
// use dataurl loader for small files and file loader for big files!
loader = contents.length <= maxSize ? "dataurl" : "file";
return { contents, loader };
}
);
},
},
// this custom plugin parses the style files
{
name: "parse-styles",
setup(build) {
build.onLoad(
// consider only .scss and .css files
{ filter: /.\.(css|scss)$/, namespace: "file" },
async (args) => {
let content;
// handle scss, convert to css
if (args.path.endsWith(".scss")) {
const result = sass.renderSync({ file: args.path });
content = result.css;
} else {
// read file content
content = await fs.readFile(args.path);
}
// postcss plugins
const plugins = [
require("tailwindcss"),
require("autoprefixer"),
// rewrite urls inside css
url({
url: "inline",
maxSize: 10, // use dataurls if files are smaller than 10k
fallback: "copy", // if files are bigger use copy method
assetsPath: "./build/assets",
useHash: true,
optimizeSvgEncode: true,
}),
];
const { css } = await postcss(plugins).process(content, {
from: args.path,
to: outdir,
});
// built-in loaders: js, jsx, ts, tsx, css, json, text, base64, dataurl, file, binary
return { contents: css, loader: "text" };
}
);
},
},
],
});
// watch and serve
if (watch || serve) {
if (watch) await ctx.watch();
if (serve) {
// generate app props based on package.json and secretProps.json
await fs.writeFile(`./${outdir}/appProps.js`, `export default ${JSON.stringify(appProps())}`);
let { host, port } = await ctx.serve({
host: "0.0.0.0",
port: parseInt(process.env.PORT),
servedir: "public",
});
console.log("serve on", `${host}:${port}`);
}
} else {
await ctx.rebuild();
await ctx.dispose();
}
};
build();