balm-core 5.2.0 → 6.0.0-alpha.1

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.
Files changed (301) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/index.cjs +2625 -0
  3. package/dist/index.cjs.map +1 -0
  4. package/dist/index.d.cts +569 -0
  5. package/dist/index.d.ts +569 -0
  6. package/dist/index.js +2527 -0
  7. package/dist/index.js.map +1 -0
  8. package/package.json +49 -123
  9. package/src/balm.ts +108 -0
  10. package/src/bundler/esbuild.ts +47 -0
  11. package/src/bundler/index.ts +3 -0
  12. package/src/bundler/rollup.ts +45 -0
  13. package/src/bundler/webpack.ts +124 -0
  14. package/src/config/constants.ts +12 -0
  15. package/src/config/defaults.ts +209 -0
  16. package/src/config/index.ts +112 -0
  17. package/src/hooks/index.ts +224 -0
  18. package/src/index.ts +16 -0
  19. package/src/pipeline/file.ts +69 -0
  20. package/src/pipeline/index.ts +10 -0
  21. package/src/pipeline/pipeline.ts +135 -0
  22. package/src/pipeline/transforms/cache.ts +146 -0
  23. package/src/pipeline/transforms/htmlmin.ts +18 -0
  24. package/src/pipeline/transforms/less.ts +75 -0
  25. package/src/pipeline/transforms/postcss.ts +43 -0
  26. package/src/pipeline/transforms/rename.ts +37 -0
  27. package/src/pipeline/transforms/replace.ts +30 -0
  28. package/src/pipeline/transforms/sass.ts +106 -0
  29. package/src/pipeline/transforms/terser.ts +20 -0
  30. package/src/runner/dag.ts +77 -0
  31. package/src/runner/task.ts +38 -0
  32. package/src/shims.d.ts +5 -0
  33. package/src/tasks/cache.ts +72 -0
  34. package/src/tasks/clean.ts +21 -0
  35. package/src/tasks/copy.ts +36 -0
  36. package/src/tasks/ftp.ts +90 -0
  37. package/src/tasks/html.ts +232 -0
  38. package/src/tasks/index.ts +14 -0
  39. package/src/tasks/publish.ts +71 -0
  40. package/src/tasks/pwa.ts +99 -0
  41. package/src/tasks/remove.ts +24 -0
  42. package/src/tasks/replace.ts +34 -0
  43. package/src/tasks/script.ts +28 -0
  44. package/src/tasks/server.ts +183 -0
  45. package/src/tasks/static.ts +104 -0
  46. package/src/tasks/style.ts +57 -0
  47. package/src/tasks/zip.ts +65 -0
  48. package/src/types/index.ts +202 -0
  49. package/src/utilities/file.ts +120 -0
  50. package/src/utilities/logger.ts +63 -0
  51. package/src/utilities/utils.ts +46 -0
  52. package/src/utilities/workspace.ts +38 -0
  53. package/src/watcher/index.ts +100 -0
  54. package/tsconfig.json +8 -0
  55. package/tsup.config.ts +12 -0
  56. package/README.md +0 -196
  57. package/index.d.ts +0 -360
  58. package/lib/balm.js +0 -49
  59. package/lib/bootstrap/check_config.js +0 -43
  60. package/lib/bootstrap/index.js +0 -87
  61. package/lib/bundler/esbuild/build.js +0 -26
  62. package/lib/bundler/esbuild/index.js +0 -7
  63. package/lib/bundler/esbuild/options.js +0 -7
  64. package/lib/bundler/esbuild/transform.js +0 -36
  65. package/lib/bundler/rollup/build.js +0 -52
  66. package/lib/bundler/rollup/entry.js +0 -28
  67. package/lib/bundler/rollup/index.js +0 -20
  68. package/lib/bundler/rollup/output.js +0 -20
  69. package/lib/bundler/rollup/plugins.js +0 -31
  70. package/lib/bundler/webpack/config/common.js +0 -135
  71. package/lib/bundler/webpack/config/dev.js +0 -12
  72. package/lib/bundler/webpack/config/env.js +0 -67
  73. package/lib/bundler/webpack/config/index.js +0 -6
  74. package/lib/bundler/webpack/config/prod.js +0 -39
  75. package/lib/bundler/webpack/config/regex.js +0 -10
  76. package/lib/bundler/webpack/entry.js +0 -65
  77. package/lib/bundler/webpack/index.js +0 -32
  78. package/lib/bundler/webpack/loaders.js +0 -50
  79. package/lib/bundler/webpack/output.js +0 -41
  80. package/lib/bundler/webpack/rules/asset.js +0 -33
  81. package/lib/bundler/webpack/rules/babel.js +0 -19
  82. package/lib/bundler/webpack/rules/css.js +0 -109
  83. package/lib/bundler/webpack/rules/html.js +0 -15
  84. package/lib/bundler/webpack/rules/index.js +0 -10
  85. package/lib/config/assets.js +0 -30
  86. package/lib/config/constants.js +0 -14
  87. package/lib/config/env.js +0 -35
  88. package/lib/config/extras.js +0 -6
  89. package/lib/config/ftp.js +0 -6
  90. package/lib/config/globals.js +0 -43
  91. package/lib/config/html.js +0 -22
  92. package/lib/config/images.js +0 -4
  93. package/lib/config/index.js +0 -63
  94. package/lib/config/logs.js +0 -11
  95. package/lib/config/paths.js +0 -46
  96. package/lib/config/pwa.js +0 -24
  97. package/lib/config/roots.js +0 -9
  98. package/lib/config/scripts.js +0 -162
  99. package/lib/config/server.js +0 -84
  100. package/lib/config/spinner.js +0 -9
  101. package/lib/config/styles.js +0 -67
  102. package/lib/hello.js +0 -19
  103. package/lib/hooks/index.js +0 -94
  104. package/lib/hooks/maker.js +0 -59
  105. package/lib/index.js +0 -5
  106. package/lib/middlewares/history.js +0 -6
  107. package/lib/middlewares/index.js +0 -11
  108. package/lib/middlewares/proxy.js +0 -28
  109. package/lib/middlewares/webpack.js +0 -26
  110. package/lib/plugins/htmlmin.js +0 -39
  111. package/lib/plugins/imagemin.js +0 -95
  112. package/lib/plugins/index.js +0 -27
  113. package/lib/plugins/jsmin.js +0 -37
  114. package/lib/plugins/less.js +0 -97
  115. package/lib/plugins/plumber.js +0 -87
  116. package/lib/plugins/postcss-plugins.js +0 -13
  117. package/lib/plugins/postcss.js +0 -117
  118. package/lib/plugins/rename.js +0 -51
  119. package/lib/plugins/replace.js +0 -91
  120. package/lib/plugins/sass-legacy.js +0 -131
  121. package/lib/plugins/sass.js +0 -134
  122. package/lib/plugins/sftp.js +0 -288
  123. package/lib/tasks/default.js +0 -46
  124. package/lib/tasks/foundation/balm.js +0 -72
  125. package/lib/tasks/foundation/balm_style.js +0 -65
  126. package/lib/tasks/foundation/index.js +0 -10
  127. package/lib/tasks/index.js +0 -48
  128. package/lib/tasks/private/build.js +0 -18
  129. package/lib/tasks/private/cache.js +0 -31
  130. package/lib/tasks/private/clean.js +0 -69
  131. package/lib/tasks/private/end.js +0 -24
  132. package/lib/tasks/private/extra.js +0 -35
  133. package/lib/tasks/private/font.js +0 -15
  134. package/lib/tasks/private/image.js +0 -37
  135. package/lib/tasks/private/index.js +0 -34
  136. package/lib/tasks/private/lint.js +0 -20
  137. package/lib/tasks/private/media.js +0 -15
  138. package/lib/tasks/private/modernizr.js +0 -59
  139. package/lib/tasks/private/pwa-cache.js +0 -32
  140. package/lib/tasks/private/script.js +0 -9
  141. package/lib/tasks/private/sprite.js +0 -105
  142. package/lib/tasks/private/start.js +0 -16
  143. package/lib/tasks/private/style.js +0 -9
  144. package/lib/tasks/private/workbox-sw.js +0 -15
  145. package/lib/tasks/public/copy.js +0 -16
  146. package/lib/tasks/public/esbuild.js +0 -18
  147. package/lib/tasks/public/ftp.js +0 -39
  148. package/lib/tasks/public/html.js +0 -65
  149. package/lib/tasks/public/index.js +0 -38
  150. package/lib/tasks/public/jsmin.js +0 -23
  151. package/lib/tasks/public/less.js +0 -21
  152. package/lib/tasks/public/postcss.js +0 -16
  153. package/lib/tasks/public/publish.js +0 -51
  154. package/lib/tasks/public/pwa.js +0 -84
  155. package/lib/tasks/public/remove.js +0 -46
  156. package/lib/tasks/public/replace.js +0 -26
  157. package/lib/tasks/public/rollup.js +0 -17
  158. package/lib/tasks/public/sass.js +0 -27
  159. package/lib/tasks/public/server.js +0 -130
  160. package/lib/tasks/public/url.js +0 -35
  161. package/lib/tasks/public/version.js +0 -23
  162. package/lib/tasks/public/webpack.js +0 -39
  163. package/lib/tasks/public/zip.js +0 -19
  164. package/lib/utilities/address.js +0 -55
  165. package/lib/utilities/cache-core.js +0 -66
  166. package/lib/utilities/color.js +0 -27
  167. package/lib/utilities/compiling.js +0 -24
  168. package/lib/utilities/detect-port.js +0 -100
  169. package/lib/utilities/escape-string-regexp.js +0 -10
  170. package/lib/utilities/file.js +0 -70
  171. package/lib/utilities/index.js +0 -8
  172. package/lib/utilities/loading.js +0 -37
  173. package/lib/utilities/logger.js +0 -99
  174. package/lib/utilities/merge-stream.js +0 -38
  175. package/lib/utilities/namespace.js +0 -11
  176. package/lib/utilities/public-url.js +0 -37
  177. package/lib/utilities/replacestream.js +0 -106
  178. package/lib/utilities/typeof.js +0 -5
  179. package/lib/utilities/utils.js +0 -43
  180. package/tslib/balm.js +0 -44
  181. package/tslib/bootstrap/check_config.js +0 -36
  182. package/tslib/bootstrap/index.js +0 -82
  183. package/tslib/bundler/esbuild/build.js +0 -30
  184. package/tslib/bundler/esbuild/index.js +0 -11
  185. package/tslib/bundler/esbuild/options.js +0 -7
  186. package/tslib/bundler/esbuild/transform.js +0 -38
  187. package/tslib/bundler/rollup/build.js +0 -17
  188. package/tslib/bundler/rollup/entry.js +0 -24
  189. package/tslib/bundler/rollup/index.js +0 -21
  190. package/tslib/bundler/rollup/output.js +0 -23
  191. package/tslib/bundler/rollup/plugins.js +0 -27
  192. package/tslib/bundler/webpack/config/common.js +0 -130
  193. package/tslib/bundler/webpack/config/dev.js +0 -14
  194. package/tslib/bundler/webpack/config/env.js +0 -44
  195. package/tslib/bundler/webpack/config/index.js +0 -8
  196. package/tslib/bundler/webpack/config/prod.js +0 -43
  197. package/tslib/bundler/webpack/config/regex.js +0 -10
  198. package/tslib/bundler/webpack/entry.js +0 -68
  199. package/tslib/bundler/webpack/index.js +0 -30
  200. package/tslib/bundler/webpack/loaders.js +0 -51
  201. package/tslib/bundler/webpack/output.js +0 -41
  202. package/tslib/bundler/webpack/rules/asset.js +0 -33
  203. package/tslib/bundler/webpack/rules/babel.js +0 -16
  204. package/tslib/bundler/webpack/rules/css.js +0 -95
  205. package/tslib/bundler/webpack/rules/html.js +0 -15
  206. package/tslib/bundler/webpack/rules/index.js +0 -10
  207. package/tslib/config/assets.js +0 -24
  208. package/tslib/config/constants.js +0 -14
  209. package/tslib/config/env.js +0 -43
  210. package/tslib/config/extras.js +0 -6
  211. package/tslib/config/ftp.js +0 -6
  212. package/tslib/config/globals.js +0 -47
  213. package/tslib/config/html.js +0 -17
  214. package/tslib/config/images.js +0 -4
  215. package/tslib/config/index.js +0 -56
  216. package/tslib/config/logs.js +0 -6
  217. package/tslib/config/paths.js +0 -30
  218. package/tslib/config/pwa.js +0 -18
  219. package/tslib/config/roots.js +0 -8
  220. package/tslib/config/scripts.js +0 -93
  221. package/tslib/config/server.js +0 -33
  222. package/tslib/config/spinner.js +0 -10
  223. package/tslib/config/styles.js +0 -37
  224. package/tslib/hello.js +0 -19
  225. package/tslib/hooks/index.js +0 -75
  226. package/tslib/hooks/maker.js +0 -66
  227. package/tslib/index.js +0 -5
  228. package/tslib/middlewares/history.js +0 -12
  229. package/tslib/middlewares/index.js +0 -18
  230. package/tslib/middlewares/proxy.js +0 -27
  231. package/tslib/middlewares/webpack.js +0 -29
  232. package/tslib/plugins/htmlmin.js +0 -38
  233. package/tslib/plugins/imagemin.js +0 -97
  234. package/tslib/plugins/index.js +0 -27
  235. package/tslib/plugins/jsmin.js +0 -39
  236. package/tslib/plugins/less.js +0 -90
  237. package/tslib/plugins/plumber.js +0 -81
  238. package/tslib/plugins/postcss-plugins.js +0 -24
  239. package/tslib/plugins/postcss.js +0 -111
  240. package/tslib/plugins/rename.js +0 -51
  241. package/tslib/plugins/replace.js +0 -72
  242. package/tslib/plugins/sass-legacy.js +0 -95
  243. package/tslib/plugins/sass.js +0 -109
  244. package/tslib/plugins/sftp.js +0 -258
  245. package/tslib/tasks/default.js +0 -59
  246. package/tslib/tasks/foundation/balm.js +0 -75
  247. package/tslib/tasks/foundation/balm_style.js +0 -68
  248. package/tslib/tasks/foundation/index.js +0 -10
  249. package/tslib/tasks/index.js +0 -43
  250. package/tslib/tasks/private/build.js +0 -15
  251. package/tslib/tasks/private/cache.js +0 -48
  252. package/tslib/tasks/private/clean.js +0 -82
  253. package/tslib/tasks/private/end.js +0 -21
  254. package/tslib/tasks/private/extra.js +0 -34
  255. package/tslib/tasks/private/font.js +0 -12
  256. package/tslib/tasks/private/image.js +0 -42
  257. package/tslib/tasks/private/index.js +0 -34
  258. package/tslib/tasks/private/lint.js +0 -17
  259. package/tslib/tasks/private/media.js +0 -12
  260. package/tslib/tasks/private/modernizr.js +0 -54
  261. package/tslib/tasks/private/pwa-cache.js +0 -32
  262. package/tslib/tasks/private/script.js +0 -9
  263. package/tslib/tasks/private/sprite.js +0 -93
  264. package/tslib/tasks/private/start.js +0 -13
  265. package/tslib/tasks/private/style.js +0 -12
  266. package/tslib/tasks/private/workbox-sw.js +0 -15
  267. package/tslib/tasks/public/copy.js +0 -18
  268. package/tslib/tasks/public/esbuild.js +0 -18
  269. package/tslib/tasks/public/ftp.js +0 -39
  270. package/tslib/tasks/public/html.js +0 -75
  271. package/tslib/tasks/public/index.js +0 -38
  272. package/tslib/tasks/public/jsmin.js +0 -24
  273. package/tslib/tasks/public/less.js +0 -21
  274. package/tslib/tasks/public/postcss.js +0 -16
  275. package/tslib/tasks/public/publish.js +0 -50
  276. package/tslib/tasks/public/pwa.js +0 -87
  277. package/tslib/tasks/public/remove.js +0 -44
  278. package/tslib/tasks/public/replace.js +0 -27
  279. package/tslib/tasks/public/rollup.js +0 -17
  280. package/tslib/tasks/public/sass.js +0 -27
  281. package/tslib/tasks/public/server.js +0 -129
  282. package/tslib/tasks/public/url.js +0 -35
  283. package/tslib/tasks/public/version.js +0 -27
  284. package/tslib/tasks/public/webpack.js +0 -41
  285. package/tslib/tasks/public/zip.js +0 -19
  286. package/tslib/utilities/address.js +0 -48
  287. package/tslib/utilities/cache-core.js +0 -57
  288. package/tslib/utilities/color.js +0 -33
  289. package/tslib/utilities/compiling.js +0 -16
  290. package/tslib/utilities/detect-port.js +0 -91
  291. package/tslib/utilities/escape-string-regexp.js +0 -7
  292. package/tslib/utilities/file.js +0 -78
  293. package/tslib/utilities/index.js +0 -8
  294. package/tslib/utilities/loading.js +0 -25
  295. package/tslib/utilities/logger.js +0 -92
  296. package/tslib/utilities/merge-stream.js +0 -34
  297. package/tslib/utilities/namespace.js +0 -12
  298. package/tslib/utilities/public-url.js +0 -31
  299. package/tslib/utilities/replacestream.js +0 -112
  300. package/tslib/utilities/typeof.js +0 -8
  301. package/tslib/utilities/utils.js +0 -38
package/dist/index.js ADDED
@@ -0,0 +1,2527 @@
1
+ // src/types/index.ts
2
+ var BundlerType = /* @__PURE__ */ ((BundlerType2) => {
3
+ BundlerType2["webpack"] = "webpack";
4
+ BundlerType2["rollup"] = "rollup";
5
+ BundlerType2["esbuild"] = "esbuild";
6
+ return BundlerType2;
7
+ })(BundlerType || {});
8
+
9
+ // src/config/index.ts
10
+ import path3 from "path";
11
+
12
+ // src/config/defaults.ts
13
+ import path from "path";
14
+ function createDefaultConfig(workspace = process.cwd()) {
15
+ const isProd = process.env.NODE_ENV === "production";
16
+ const isDev = !isProd;
17
+ const isTest = process.env.NODE_ENV === "test";
18
+ const roots = {
19
+ source: "src",
20
+ tmp: ".tmp",
21
+ target: "dist"
22
+ };
23
+ const paths = {
24
+ source: {
25
+ base: "",
26
+ html: "",
27
+ css: "styles",
28
+ js: "scripts",
29
+ img: "images",
30
+ font: "fonts",
31
+ media: "media"
32
+ },
33
+ tmp: {
34
+ base: "",
35
+ css: "css",
36
+ js: "js",
37
+ img: "img",
38
+ font: "fonts",
39
+ media: "media"
40
+ },
41
+ target: {
42
+ base: "",
43
+ css: "css",
44
+ js: "js",
45
+ img: "img",
46
+ font: "font",
47
+ media: "media"
48
+ }
49
+ };
50
+ const alias = {
51
+ "@": path.join(workspace, roots.source)
52
+ };
53
+ const styles = {
54
+ extname: "scss",
55
+ minify: false,
56
+ atImportPaths: [],
57
+ alias: {},
58
+ options: {},
59
+ sassOptions: {},
60
+ lessOptions: {},
61
+ postcssLoaderOptions: {},
62
+ postcssPlugins: [],
63
+ sprites: [],
64
+ spriteOptions: {}
65
+ };
66
+ const scripts = {
67
+ bundler: "webpack" /* webpack */,
68
+ minify: false,
69
+ minifyOptions: {},
70
+ entry: "",
71
+ target: "web",
72
+ library: "",
73
+ libraryTarget: "var",
74
+ externals: {},
75
+ injectHtml: false,
76
+ htmlName: "index.html",
77
+ alias: {},
78
+ plugins: [],
79
+ loaders: [],
80
+ defaultLoaders: {
81
+ html: true,
82
+ css: true,
83
+ js: true,
84
+ url: true
85
+ },
86
+ options: {},
87
+ esbuildOptions: {},
88
+ rollupOptions: {},
89
+ webpackOptions: {},
90
+ useCache: false
91
+ };
92
+ const html = {
93
+ options: {
94
+ collapseWhitespace: true,
95
+ minifyCSS: true,
96
+ minifyJS: {
97
+ compress: {
98
+ drop_console: true
99
+ }
100
+ },
101
+ processConditionalComments: true,
102
+ removeComments: true,
103
+ removeEmptyAttributes: true,
104
+ removeScriptTypeAttributes: true,
105
+ removeStyleLinkTypeAttributes: true
106
+ }
107
+ };
108
+ const assets = {
109
+ root: "assets",
110
+ mainDir: "public",
111
+ subDir: "",
112
+ buildDir: "build",
113
+ virtualDir: "",
114
+ cache: false,
115
+ options: {
116
+ fileNameManifest: "rev-manifest.json",
117
+ dontRenameFile: [".html"],
118
+ dontUpdateReference: []
119
+ },
120
+ includes: [],
121
+ excludes: []
122
+ };
123
+ const server = {
124
+ host: null,
125
+ port: 3e3,
126
+ open: true,
127
+ proxy: false,
128
+ proxyOptions: false,
129
+ historyOptions: false,
130
+ useHMR: false,
131
+ options: {}
132
+ };
133
+ const pwa = {
134
+ enabled: false,
135
+ mode: "generateSW",
136
+ version: "",
137
+ manifest: "manifest.json",
138
+ swSrcFilename: "service-worker.js",
139
+ swDestFilename: "service-worker.js",
140
+ options: {}
141
+ };
142
+ const ftp = {
143
+ options: {},
144
+ watchFiles: []
145
+ };
146
+ const images = {
147
+ plugins: []
148
+ };
149
+ const env = {
150
+ isProd,
151
+ isDev,
152
+ isTest,
153
+ inFrontend: true,
154
+ inSSR: false,
155
+ inDesktopApp: false,
156
+ isMP: false
157
+ };
158
+ const src = {
159
+ base: path.join(workspace, roots.source),
160
+ html: path.join(workspace, roots.source, paths.source.html),
161
+ css: path.join(workspace, roots.source, paths.source.css),
162
+ js: path.join(workspace, roots.source, paths.source.js),
163
+ img: path.join(workspace, roots.source, paths.source.img),
164
+ font: path.join(workspace, roots.source, paths.source.font),
165
+ media: path.join(workspace, roots.source, paths.source.media)
166
+ };
167
+ const targetBase = path.join(workspace, isProd ? roots.target : roots.tmp);
168
+ const dest = {
169
+ base: targetBase,
170
+ html: targetBase,
171
+ css: path.join(targetBase, isProd ? paths.target.css : paths.tmp.css),
172
+ js: path.join(targetBase, isProd ? paths.target.js : paths.tmp.js),
173
+ img: path.join(targetBase, isProd ? paths.target.img : paths.tmp.img),
174
+ font: path.join(targetBase, isProd ? paths.target.font : paths.tmp.font),
175
+ media: path.join(targetBase, isProd ? paths.target.media : paths.tmp.media),
176
+ static: path.join(targetBase, assets.virtualDir, assets.subDir)
177
+ };
178
+ return {
179
+ workspace,
180
+ workspaces: {
181
+ local: workspace,
182
+ global: path.resolve(workspace, "..")
183
+ },
184
+ alias,
185
+ env,
186
+ inFrontend: true,
187
+ useDefaults: true,
188
+ roots,
189
+ paths,
190
+ styles,
191
+ scripts,
192
+ html,
193
+ assets,
194
+ server,
195
+ pwa,
196
+ ftp,
197
+ images,
198
+ logs: {
199
+ level: 1
200
+ },
201
+ src,
202
+ dest
203
+ };
204
+ }
205
+
206
+ // src/utilities/utils.ts
207
+ function isObject(val) {
208
+ return val !== null && typeof val === "object" && !Array.isArray(val);
209
+ }
210
+ function isArray(val) {
211
+ return Array.isArray(val);
212
+ }
213
+ function isString(val) {
214
+ return typeof val === "string";
215
+ }
216
+ function isFunction(val) {
217
+ return typeof val === "function";
218
+ }
219
+ function isBoolean(val) {
220
+ return typeof val === "boolean";
221
+ }
222
+ function deepMerge(target, ...sources) {
223
+ if (!sources.length) return target;
224
+ const source = sources.shift();
225
+ if (isObject(target) && isObject(source)) {
226
+ for (const key in source) {
227
+ if (isObject(source[key])) {
228
+ if (!target[key]) Object.assign(target, { [key]: {} });
229
+ deepMerge(target[key], source[key]);
230
+ } else if (isArray(source[key])) {
231
+ target[key] = [...source[key]];
232
+ } else if (source[key] !== void 0) {
233
+ Object.assign(target, { [key]: source[key] });
234
+ }
235
+ }
236
+ }
237
+ return deepMerge(target, ...sources);
238
+ }
239
+ function toNamespace(name, namespace = "balm") {
240
+ if (isArray(name)) {
241
+ return name.map((item) => `${namespace}:${item}`);
242
+ }
243
+ return `${namespace}:${name}`;
244
+ }
245
+
246
+ // src/config/constants.ts
247
+ var ASSETS_TYPES = ["css", "js", "img", "font", "media"];
248
+ var PUBLIC_URL = "%PUBLIC_URL%";
249
+ var CHUNK = { dir: "chunk" };
250
+ var ASSET = { dir: "asset" };
251
+ var ASSET_EXT = {
252
+ styles: ["css", "scss", "sass", "less"],
253
+ scripts: ["js", "mjs", "cjs", "ts", "mts", "cts", "jsx", "tsx"],
254
+ images: ["png", "jpg", "jpeg", "gif", "svg", "webp", "ico"],
255
+ fonts: ["woff", "woff2", "ttf", "eot", "otf"],
256
+ media: ["mp4", "webm", "ogg", "mp3", "wav", "flac", "aac"]
257
+ };
258
+
259
+ // src/utilities/workspace.ts
260
+ import path2 from "path";
261
+ var currentLocalWorkspace = process.cwd();
262
+ var currentGlobalWorkspace = path2.resolve(process.cwd(), "..");
263
+ function setWorkspaces(local, global) {
264
+ if (local) {
265
+ currentLocalWorkspace = path2.resolve(local);
266
+ }
267
+ if (global) {
268
+ currentGlobalWorkspace = path2.resolve(global);
269
+ } else if (local && !global) {
270
+ currentGlobalWorkspace = path2.resolve(currentLocalWorkspace, "..");
271
+ }
272
+ }
273
+ function getWorkspaces() {
274
+ return {
275
+ local: currentLocalWorkspace,
276
+ global: currentGlobalWorkspace
277
+ };
278
+ }
279
+ function localWorkspace(subPath = "") {
280
+ return subPath ? path2.resolve(currentLocalWorkspace, subPath) : currentLocalWorkspace;
281
+ }
282
+ function globalWorkspace(subPath = "") {
283
+ return subPath ? path2.resolve(currentGlobalWorkspace, subPath) : currentGlobalWorkspace;
284
+ }
285
+ var localResolve = localWorkspace;
286
+ var globalResolve = globalWorkspace;
287
+
288
+ // src/config/index.ts
289
+ function resolveConfig(customConfig = {}, defaultWorkspace = process.cwd()) {
290
+ let localWorkspace2 = defaultWorkspace;
291
+ let globalWorkspace2 = path3.resolve(defaultWorkspace, "..");
292
+ if (customConfig.workspace) {
293
+ if (typeof customConfig.workspace === "string") {
294
+ localWorkspace2 = path3.resolve(customConfig.workspace);
295
+ globalWorkspace2 = path3.resolve(localWorkspace2, "..");
296
+ } else if (typeof customConfig.workspace === "object") {
297
+ localWorkspace2 = path3.resolve(customConfig.workspace.local || defaultWorkspace);
298
+ globalWorkspace2 = path3.resolve(
299
+ customConfig.workspace.global || path3.resolve(localWorkspace2, "..")
300
+ );
301
+ }
302
+ }
303
+ setWorkspaces(localWorkspace2, globalWorkspace2);
304
+ const defaultConfig = createDefaultConfig(localWorkspace2);
305
+ const config = deepMerge(defaultConfig, customConfig);
306
+ config.workspace = localWorkspace2;
307
+ config.workspaces = {
308
+ local: localWorkspace2,
309
+ global: globalWorkspace2
310
+ };
311
+ config.src = {
312
+ base: path3.join(config.workspace, config.roots.source),
313
+ html: path3.join(config.workspace, config.roots.source, config.paths.source.html),
314
+ css: path3.join(config.workspace, config.roots.source, config.paths.source.css),
315
+ js: path3.join(config.workspace, config.roots.source, config.paths.source.js),
316
+ img: path3.join(config.workspace, config.roots.source, config.paths.source.img),
317
+ font: path3.join(config.workspace, config.roots.source, config.paths.source.font),
318
+ media: path3.join(config.workspace, config.roots.source, config.paths.source.media)
319
+ };
320
+ const targetBase = path3.join(
321
+ config.workspace,
322
+ config.env.isProd || !config.inFrontend ? config.roots.target : config.roots.tmp
323
+ );
324
+ const isProd = config.env.isProd || !config.inFrontend;
325
+ const pathMap = isProd ? config.paths.target : config.paths.tmp;
326
+ const destStatic = path3.join(
327
+ targetBase,
328
+ config.assets.virtualDir,
329
+ config.assets.subDir
330
+ );
331
+ config.dest = {
332
+ base: targetBase,
333
+ html: targetBase,
334
+ css: path3.join(targetBase, pathMap.css),
335
+ js: path3.join(targetBase, pathMap.js),
336
+ img: path3.join(targetBase, pathMap.img),
337
+ font: path3.join(targetBase, pathMap.font),
338
+ media: path3.join(targetBase, pathMap.media),
339
+ static: destStatic
340
+ };
341
+ config.assets.static = path3.join(
342
+ config.assets.root,
343
+ config.assets.mainDir,
344
+ config.assets.subDir
345
+ );
346
+ for (const assetType of ASSETS_TYPES) {
347
+ config.assets[assetType] = path3.join(
348
+ config.assets.static,
349
+ config.paths.target[assetType]
350
+ );
351
+ }
352
+ const hmrEnv = config.useDefaults && config.env.isDev && !config.env.inSSR && !config.env.isMP;
353
+ const hmrEnabling = config.scripts.bundler === "webpack";
354
+ config.server.useHMR = hmrEnv && hmrEnabling;
355
+ if (/^electron-.*/.test(config.scripts.target)) {
356
+ config.env.inDesktopApp = true;
357
+ }
358
+ config.scripts.useCache = config.env.isProd && config.assets.cache && config.scripts.injectHtml;
359
+ return config;
360
+ }
361
+
362
+ // src/utilities/logger.ts
363
+ import pc from "picocolors";
364
+ var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
365
+ LogLevel2[LogLevel2["Debug"] = 0] = "Debug";
366
+ LogLevel2[LogLevel2["Info"] = 1] = "Info";
367
+ LogLevel2[LogLevel2["Warn"] = 2] = "Warn";
368
+ LogLevel2[LogLevel2["Error"] = 3] = "Error";
369
+ LogLevel2[LogLevel2["Silent"] = 4] = "Silent";
370
+ return LogLevel2;
371
+ })(LogLevel || {});
372
+ var Logger = class {
373
+ level = 1 /* Info */;
374
+ formatTag(tag) {
375
+ return tag ? pc.cyan(`[${tag}]`) : pc.bgBlue(pc.black(" BalmJS "));
376
+ }
377
+ timestamp() {
378
+ const d = /* @__PURE__ */ new Date();
379
+ const pad = (n) => n.toString().padStart(2, "0");
380
+ return pc.gray(`[${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}]`);
381
+ }
382
+ debug(tag, message, ...args) {
383
+ if (this.level <= 0 /* Debug */) {
384
+ console.log(this.timestamp(), this.formatTag(tag), pc.magenta("\u203B"), message, ...args);
385
+ }
386
+ }
387
+ info(tag, message, ...args) {
388
+ if (this.level <= 1 /* Info */) {
389
+ console.log(this.timestamp(), this.formatTag(tag), pc.blue("\u2139"), message, ...args);
390
+ }
391
+ }
392
+ success(tag, message, ...args) {
393
+ if (this.level <= 1 /* Info */) {
394
+ console.log(this.timestamp(), this.formatTag(tag), pc.green("\u2714"), pc.green(message), ...args);
395
+ }
396
+ }
397
+ warn(tag, message, ...args) {
398
+ if (this.level <= 2 /* Warn */) {
399
+ console.warn(this.timestamp(), this.formatTag(tag), pc.yellow("\u26A0"), pc.yellow(message), ...args);
400
+ }
401
+ }
402
+ error(tag, message, ...args) {
403
+ if (this.level <= 3 /* Error */) {
404
+ console.error(this.timestamp(), this.formatTag(tag), pc.red("\u2716"), pc.red(message), ...args);
405
+ }
406
+ }
407
+ time(label) {
408
+ const start = performance.now();
409
+ return () => {
410
+ const duration = (performance.now() - start).toFixed(2);
411
+ this.info("time", `${label} completed in ${pc.bold(duration + "ms")}`);
412
+ };
413
+ }
414
+ };
415
+ var logger = new Logger();
416
+
417
+ // src/runner/dag.ts
418
+ import pc2 from "picocolors";
419
+ var TaskDAG = class {
420
+ tasks = /* @__PURE__ */ new Map();
421
+ registerTask(task) {
422
+ this.tasks.set(task.name, task);
423
+ }
424
+ getTask(name) {
425
+ return this.tasks.get(name);
426
+ }
427
+ hasTask(name) {
428
+ return this.tasks.has(name);
429
+ }
430
+ clear() {
431
+ this.tasks.clear();
432
+ }
433
+ /**
434
+ * Run a list of tasks in strict series
435
+ */
436
+ async runSeries(taskNames, config) {
437
+ for (const name of taskNames) {
438
+ await this.executeTask(name, config);
439
+ }
440
+ }
441
+ /**
442
+ * Run a list of tasks in parallel
443
+ */
444
+ async runParallel(taskNames, config) {
445
+ await Promise.all(taskNames.map((name) => this.executeTask(name, config)));
446
+ }
447
+ /**
448
+ * Execute a single task resolving its dependencies first
449
+ */
450
+ async executeTask(taskName, config, executed = /* @__PURE__ */ new Set()) {
451
+ if (executed.has(taskName)) return;
452
+ const task = this.tasks.get(taskName);
453
+ if (!task) {
454
+ logger.warn("task", `Task ${pc2.bold(taskName)} not found, skipping.`);
455
+ return;
456
+ }
457
+ if (task.deps && task.deps.length > 0) {
458
+ for (const dep of task.deps) {
459
+ await this.executeTask(dep, config, executed);
460
+ }
461
+ }
462
+ const start = performance.now();
463
+ try {
464
+ await task.run(config);
465
+ executed.add(taskName);
466
+ const duration = (performance.now() - start).toFixed(1);
467
+ if (!task.hidden) {
468
+ logger.info("task", `${pc2.cyan(task.name)} finished in ${pc2.bold(duration + "ms")}`);
469
+ }
470
+ } catch (error) {
471
+ logger.error("task", `Task ${pc2.bold(task.name)} failed: ${error?.message || error}`);
472
+ throw error;
473
+ }
474
+ }
475
+ };
476
+
477
+ // src/tasks/clean.ts
478
+ import path5 from "path";
479
+
480
+ // src/runner/task.ts
481
+ var BaseTask = class {
482
+ name;
483
+ deps;
484
+ description;
485
+ hidden;
486
+ constructor(options) {
487
+ this.name = options.name;
488
+ this.deps = options.deps || [];
489
+ this.description = options.description;
490
+ this.hidden = options.hidden || false;
491
+ }
492
+ };
493
+ var FunctionTask = class extends BaseTask {
494
+ fn;
495
+ constructor(name, fn, deps = []) {
496
+ super({ name, deps });
497
+ this.fn = fn;
498
+ }
499
+ async run(config) {
500
+ await this.fn(config);
501
+ }
502
+ };
503
+
504
+ // src/utilities/file.ts
505
+ import fs from "fs/promises";
506
+ import { existsSync } from "fs";
507
+ import path4 from "path";
508
+ import crypto from "crypto";
509
+ import fg from "fast-glob";
510
+ var FileSystem = class {
511
+ workspace = process.cwd();
512
+ resolveApp(...paths) {
513
+ return path4.resolve(this.workspace, ...paths);
514
+ }
515
+ absPath(filePath) {
516
+ return path4.isAbsolute(filePath) ? filePath : this.resolveApp(filePath);
517
+ }
518
+ absPaths(filePaths) {
519
+ if (Array.isArray(filePaths)) {
520
+ return filePaths.map((p) => this.absPath(p));
521
+ }
522
+ return this.absPath(filePaths);
523
+ }
524
+ async exists(targetPath) {
525
+ try {
526
+ await fs.access(this.absPath(targetPath));
527
+ return true;
528
+ } catch {
529
+ return false;
530
+ }
531
+ }
532
+ existsSync(targetPath) {
533
+ return existsSync(this.absPath(targetPath));
534
+ }
535
+ async ensureDir(dirPath) {
536
+ await fs.mkdir(this.absPath(dirPath), { recursive: true });
537
+ }
538
+ async readFile(filePath, encoding = "utf8") {
539
+ return fs.readFile(this.absPath(filePath), { encoding });
540
+ }
541
+ async readBuffer(filePath) {
542
+ return fs.readFile(this.absPath(filePath));
543
+ }
544
+ async writeFile(filePath, data) {
545
+ const fullPath = this.absPath(filePath);
546
+ await this.ensureDir(path4.dirname(fullPath));
547
+ await fs.writeFile(fullPath, data);
548
+ }
549
+ async remove(targetPath) {
550
+ const targets = Array.isArray(targetPath) ? targetPath : [targetPath];
551
+ const removed = [];
552
+ for (const item of targets) {
553
+ const fullPath = this.absPath(item);
554
+ try {
555
+ const stat = await fs.stat(fullPath);
556
+ if (stat.isDirectory()) {
557
+ await fs.rm(fullPath, { recursive: true, force: true });
558
+ } else {
559
+ await fs.unlink(fullPath);
560
+ }
561
+ removed.push(fullPath);
562
+ } catch {
563
+ }
564
+ }
565
+ return removed;
566
+ }
567
+ async copyFile(src, dest) {
568
+ const srcPath = this.absPath(src);
569
+ const destPath = this.absPath(dest);
570
+ await this.ensureDir(path4.dirname(destPath));
571
+ await fs.copyFile(srcPath, destPath);
572
+ }
573
+ async copyDir(srcDir, destDir) {
574
+ const src = this.absPath(srcDir);
575
+ const dest = this.absPath(destDir);
576
+ await this.ensureDir(dest);
577
+ const entries = await fs.readdir(src, { withFileTypes: true });
578
+ for (const entry of entries) {
579
+ const srcItem = path4.join(src, entry.name);
580
+ const destItem = path4.join(dest, entry.name);
581
+ if (entry.isDirectory()) {
582
+ await this.copyDir(srcItem, destItem);
583
+ } else {
584
+ await this.copyFile(srcItem, destItem);
585
+ }
586
+ }
587
+ }
588
+ async matchFiles(patterns, options = {}) {
589
+ const defaultOptions = {
590
+ cwd: this.workspace,
591
+ absolute: true,
592
+ dot: true,
593
+ onlyFiles: true
594
+ };
595
+ return fg(patterns, { ...defaultOptions, ...options });
596
+ }
597
+ hash(content, length = 8) {
598
+ return crypto.createHash("md5").update(content).digest("hex").slice(0, length);
599
+ }
600
+ };
601
+ var file = new FileSystem();
602
+
603
+ // src/tasks/clean.ts
604
+ var CleanTask = class extends BaseTask {
605
+ constructor() {
606
+ super({ name: "clean", description: "Clean output directories" });
607
+ }
608
+ async run(config) {
609
+ const targets = [
610
+ path5.join(config.workspace, config.roots.tmp),
611
+ path5.join(config.workspace, config.roots.target)
612
+ ];
613
+ const deleted = await file.remove(targets);
614
+ logger.info("clean", `Deleted ${deleted.length} directories`);
615
+ }
616
+ };
617
+
618
+ // src/tasks/style.ts
619
+ import path10 from "path";
620
+
621
+ // src/pipeline/pipeline.ts
622
+ import path7 from "path";
623
+ import fg2 from "fast-glob";
624
+ import fs2 from "fs/promises";
625
+
626
+ // src/pipeline/file.ts
627
+ import path6 from "path";
628
+ var VirtualFile = class _VirtualFile {
629
+ cwd;
630
+ base;
631
+ path;
632
+ contents;
633
+ revPathOriginal;
634
+ constructor(options) {
635
+ this.cwd = options.cwd;
636
+ this.base = options.base;
637
+ this.path = options.path;
638
+ this.contents = typeof options.contents === "string" ? Buffer.from(options.contents) : options.contents;
639
+ }
640
+ get relative() {
641
+ return path6.relative(this.base, this.path);
642
+ }
643
+ get extname() {
644
+ return path6.extname(this.path);
645
+ }
646
+ set extname(val) {
647
+ const dir = path6.dirname(this.path);
648
+ const basename = path6.basename(this.path, this.extname);
649
+ this.path = path6.join(dir, `${basename}${val.startsWith(".") ? val : `.${val}`}`);
650
+ }
651
+ get basename() {
652
+ return path6.basename(this.path);
653
+ }
654
+ set basename(val) {
655
+ const dir = path6.dirname(this.path);
656
+ this.path = path6.join(dir, val);
657
+ }
658
+ get stem() {
659
+ return path6.basename(this.path, this.extname);
660
+ }
661
+ set stem(val) {
662
+ const dir = path6.dirname(this.path);
663
+ this.path = path6.join(dir, `${val}${this.extname}`);
664
+ }
665
+ toString(encoding = "utf8") {
666
+ return this.contents.toString(encoding);
667
+ }
668
+ clone() {
669
+ const cloned = new _VirtualFile({
670
+ cwd: this.cwd,
671
+ base: this.base,
672
+ path: this.path,
673
+ contents: Buffer.from(this.contents)
674
+ });
675
+ cloned.revPathOriginal = this.revPathOriginal;
676
+ return cloned;
677
+ }
678
+ };
679
+
680
+ // src/pipeline/pipeline.ts
681
+ var Pipeline = class _Pipeline {
682
+ cwd;
683
+ base;
684
+ allowEmpty;
685
+ files = [];
686
+ transforms = [];
687
+ constructor(options = {}) {
688
+ this.cwd = options.cwd || process.cwd();
689
+ this.base = options.base;
690
+ this.allowEmpty = options.allowEmpty ?? true;
691
+ }
692
+ static from(patterns, options = {}) {
693
+ const pipeline = new _Pipeline(options);
694
+ return pipeline.src(patterns);
695
+ }
696
+ src(patterns, options = {}) {
697
+ if (options.cwd) this.cwd = options.cwd;
698
+ if (options.base) this.base = options.base;
699
+ if (options.allowEmpty !== void 0) this.allowEmpty = options.allowEmpty;
700
+ this.transforms.push(async () => {
701
+ const patternArr = Array.isArray(patterns) ? patterns : [patterns];
702
+ const posPatterns = patternArr.filter((p) => !p.startsWith("!"));
703
+ const ignorePatterns = patternArr.filter((p) => p.startsWith("!")).map((p) => p.slice(1));
704
+ const fileList = [];
705
+ for (const p of posPatterns) {
706
+ let patternBase = this.base;
707
+ if (!patternBase) {
708
+ const globIdx = p.search(/[*?{[\\]/);
709
+ if (globIdx !== -1) {
710
+ patternBase = path7.resolve(this.cwd, p.slice(0, globIdx));
711
+ } else {
712
+ patternBase = path7.resolve(this.cwd, path7.dirname(p));
713
+ }
714
+ }
715
+ const matched = await fg2(p, {
716
+ cwd: this.cwd,
717
+ absolute: true,
718
+ dot: true,
719
+ onlyFiles: true,
720
+ ignore: ignorePatterns
721
+ });
722
+ for (const filePath of matched) {
723
+ const buffer = await fs2.readFile(filePath);
724
+ fileList.push(
725
+ new VirtualFile({
726
+ cwd: this.cwd,
727
+ base: patternBase,
728
+ path: filePath,
729
+ contents: buffer
730
+ })
731
+ );
732
+ }
733
+ }
734
+ if (!fileList.length && !this.allowEmpty) {
735
+ throw new Error(`File not found with pattern: ${patterns}`);
736
+ }
737
+ this.files = fileList;
738
+ });
739
+ return this;
740
+ }
741
+ pipe(transform) {
742
+ this.transforms.push(transform);
743
+ return this;
744
+ }
745
+ async process() {
746
+ let isSrcLoaded = false;
747
+ for (const transform of this.transforms) {
748
+ if (!isSrcLoaded) {
749
+ await transform(null);
750
+ isSrcLoaded = true;
751
+ } else {
752
+ if (this.files.length === 0) break;
753
+ const nextFiles = [];
754
+ for (const file2 of this.files) {
755
+ const result = await transform(file2);
756
+ if (result === void 0 || result === file2) {
757
+ nextFiles.push(file2);
758
+ } else if (Array.isArray(result)) {
759
+ nextFiles.push(...result);
760
+ } else if (result !== null) {
761
+ nextFiles.push(result);
762
+ }
763
+ }
764
+ this.files = nextFiles;
765
+ }
766
+ }
767
+ return this.files;
768
+ }
769
+ async dest(outDir) {
770
+ const files = await this.process();
771
+ const writtenPaths = [];
772
+ const absOutDir = path7.isAbsolute(outDir) ? outDir : path7.resolve(this.cwd, outDir);
773
+ for (const file2 of files) {
774
+ const targetPath = path7.resolve(absOutDir, file2.relative);
775
+ await file.writeFile(targetPath, file2.contents);
776
+ writtenPaths.push(targetPath);
777
+ }
778
+ return writtenPaths;
779
+ }
780
+ };
781
+
782
+ // src/pipeline/transforms/sass.ts
783
+ import * as sass from "sass";
784
+ import path8 from "path";
785
+ import fs3 from "fs";
786
+ function resolveFile(baseDir, url) {
787
+ const possiblePaths = [
788
+ path8.resolve(baseDir, url),
789
+ path8.resolve(baseDir, `${url}.scss`),
790
+ path8.resolve(baseDir, `${url}.sass`),
791
+ path8.resolve(baseDir, `${url}.css`),
792
+ path8.resolve(baseDir, path8.dirname(url), `_${path8.basename(url)}.scss`),
793
+ path8.resolve(baseDir, path8.dirname(url), `_${path8.basename(url)}.sass`),
794
+ path8.resolve(baseDir, url, "_index.scss"),
795
+ path8.resolve(baseDir, url, "_index.sass"),
796
+ path8.resolve(baseDir, url, "index.scss"),
797
+ path8.resolve(baseDir, url, "index.sass")
798
+ ];
799
+ for (const p of possiblePaths) {
800
+ try {
801
+ if (fs3.existsSync(p) && fs3.statSync(p).isFile()) {
802
+ return p;
803
+ }
804
+ } catch {
805
+ }
806
+ }
807
+ return null;
808
+ }
809
+ function transformSass(options = {}) {
810
+ return async (file2) => {
811
+ if (!/\.(scss|sass)$/i.test(file2.extname)) return file2;
812
+ if (path8.basename(file2.path).startsWith("_")) return null;
813
+ try {
814
+ const searchDirs = [
815
+ path8.dirname(file2.path),
816
+ path8.resolve(file2.cwd, "node_modules"),
817
+ path8.resolve(process.cwd(), "node_modules"),
818
+ path8.resolve(process.cwd(), "..", "node_modules"),
819
+ ...options.includePaths || []
820
+ ];
821
+ const customImporter = {
822
+ findFileUrl(url) {
823
+ let cleanUrl = url;
824
+ if (cleanUrl.startsWith("~")) {
825
+ cleanUrl = cleanUrl.slice(1);
826
+ }
827
+ if (options.alias) {
828
+ for (const [aliasKey, aliasTarget] of Object.entries(options.alias)) {
829
+ if (cleanUrl === aliasKey) {
830
+ const resolved = resolveFile(path8.dirname(aliasTarget), path8.basename(aliasTarget));
831
+ if (resolved) return new URL(`file://${resolved}`);
832
+ } else if (cleanUrl.startsWith(`${aliasKey}/`)) {
833
+ const subPath = cleanUrl.slice(aliasKey.length + 1);
834
+ const resolved = resolveFile(aliasTarget, subPath);
835
+ if (resolved) return new URL(`file://${resolved}`);
836
+ }
837
+ }
838
+ }
839
+ for (const dir of searchDirs) {
840
+ const resolved = resolveFile(dir, cleanUrl);
841
+ if (resolved) {
842
+ return new URL(`file://${resolved}`);
843
+ }
844
+ }
845
+ return null;
846
+ }
847
+ };
848
+ const result = await sass.compileStringAsync(file2.toString(), {
849
+ url: new URL(`file://${file2.path}`),
850
+ syntax: file2.extname === ".sass" ? "indented" : "scss",
851
+ loadPaths: searchDirs,
852
+ importers: [customImporter, new sass.NodePackageImporter()],
853
+ style: options.style || "expanded",
854
+ sourceMap: options.sourceMap || false
855
+ });
856
+ file2.contents = Buffer.from(result.css);
857
+ file2.extname = ".css";
858
+ return file2;
859
+ } catch (err) {
860
+ logger.error("sass", `Error compiling ${file2.path}: ${err.message}`);
861
+ throw err;
862
+ }
863
+ };
864
+ }
865
+
866
+ // src/pipeline/transforms/less.ts
867
+ import less from "less";
868
+ import path9 from "path";
869
+ var AliasFileManager = class extends less.FileManager {
870
+ alias;
871
+ constructor(alias = {}) {
872
+ super();
873
+ this.alias = alias;
874
+ }
875
+ supports(filename) {
876
+ const clean = filename.startsWith("~") ? filename.slice(1) : filename;
877
+ return Object.keys(this.alias).some((k) => clean === k || clean.startsWith(`${k}/`));
878
+ }
879
+ loadFile(filename, currentDirectory, options, environment) {
880
+ const clean = filename.startsWith("~") ? filename.slice(1) : filename;
881
+ for (const [k, v] of Object.entries(this.alias)) {
882
+ if (clean === k) {
883
+ return super.loadFile(v, currentDirectory, options, environment);
884
+ } else if (clean.startsWith(`${k}/`)) {
885
+ const sub = clean.slice(k.length + 1);
886
+ return super.loadFile(path9.join(v, sub), currentDirectory, options, environment);
887
+ }
888
+ }
889
+ return super.loadFile(filename, currentDirectory, options, environment);
890
+ }
891
+ };
892
+ function transformLess(options = {}) {
893
+ return async (file2) => {
894
+ if (!/\.less$/i.test(file2.extname)) return file2;
895
+ if (path9.basename(file2.path).startsWith("_")) return null;
896
+ try {
897
+ const paths = [
898
+ path9.dirname(file2.path),
899
+ path9.resolve(file2.cwd, "node_modules"),
900
+ ...options.paths || []
901
+ ];
902
+ const plugins = [...options.plugins || []];
903
+ if (options.alias && Object.keys(options.alias).length > 0) {
904
+ plugins.push({
905
+ install: (lessInstance, pluginManager) => {
906
+ pluginManager.addFileManager(new AliasFileManager(options.alias));
907
+ }
908
+ });
909
+ }
910
+ const result = await less.render(file2.toString(), {
911
+ filename: file2.path,
912
+ paths,
913
+ ...options,
914
+ plugins
915
+ });
916
+ file2.contents = Buffer.from(result.css);
917
+ file2.extname = ".css";
918
+ return file2;
919
+ } catch (err) {
920
+ logger.error("less", `Error compiling ${file2.path}: ${err.message}`);
921
+ throw err;
922
+ }
923
+ };
924
+ }
925
+
926
+ // src/pipeline/transforms/postcss.ts
927
+ import postcss from "postcss";
928
+ import autoprefixer from "autoprefixer";
929
+ import postcssPresetEnv from "postcss-preset-env";
930
+ import cssnano from "cssnano";
931
+ function transformPostcss(options = {}) {
932
+ const plugins = [
933
+ ...options.plugins || [],
934
+ autoprefixer(),
935
+ postcssPresetEnv()
936
+ ];
937
+ if (options.minify) {
938
+ plugins.push(cssnano({ preset: "default" }));
939
+ }
940
+ const processor = postcss(plugins);
941
+ return async (file2) => {
942
+ if (!/\.css$/i.test(file2.extname)) return file2;
943
+ try {
944
+ const result = await processor.process(file2.toString(), {
945
+ from: file2.path,
946
+ to: file2.path
947
+ });
948
+ file2.contents = Buffer.from(result.css);
949
+ return file2;
950
+ } catch (err) {
951
+ logger.error("postcss", `Error processing ${file2.path}: ${err.message}`);
952
+ throw err;
953
+ }
954
+ };
955
+ }
956
+
957
+ // src/tasks/style.ts
958
+ var StyleTask = class extends BaseTask {
959
+ constructor() {
960
+ super({ name: "style", description: "Compile stylesheets" });
961
+ }
962
+ async run(config) {
963
+ const ext = config.styles.extname;
964
+ const srcPattern = path10.join(config.src.css, `**/*.${ext}`);
965
+ const isProd = config.env.isProd;
966
+ const pipeline = Pipeline.from([srcPattern, `!${path10.join(config.src.css, "**/_*.scss")}`, `!${path10.join(config.src.css, "**/_*.less")}`], {
967
+ cwd: config.workspace,
968
+ base: config.src.css
969
+ });
970
+ const styleAlias = {
971
+ ...config.alias,
972
+ ...config.styles.alias
973
+ };
974
+ if (ext === "scss" || ext === "sass") {
975
+ pipeline.pipe(
976
+ transformSass({
977
+ includePaths: [config.src.css, ...config.styles.atImportPaths],
978
+ alias: styleAlias,
979
+ ...config.styles.sassOptions
980
+ })
981
+ );
982
+ } else if (ext === "less") {
983
+ pipeline.pipe(
984
+ transformLess({
985
+ paths: [config.src.css, ...config.styles.atImportPaths],
986
+ alias: styleAlias,
987
+ ...config.styles.lessOptions
988
+ })
989
+ );
990
+ }
991
+ pipeline.pipe(
992
+ transformPostcss({
993
+ plugins: config.styles.postcssPlugins,
994
+ minify: isProd || config.styles.minify,
995
+ ...config.styles.postcssLoaderOptions
996
+ })
997
+ );
998
+ await pipeline.dest(config.dest.css);
999
+ }
1000
+ };
1001
+
1002
+ // src/bundler/webpack.ts
1003
+ import webpack from "webpack";
1004
+ import path11 from "path";
1005
+ async function runWebpack(config, customOptions = {}) {
1006
+ const isProd = config.env.isProd;
1007
+ let entry = customOptions.entry || config.scripts.entry;
1008
+ if (!entry) {
1009
+ entry = path11.join(config.src.js, "main.js");
1010
+ }
1011
+ const rawOutputPath = customOptions.output?.path;
1012
+ const isCustomOutput = Boolean(rawOutputPath);
1013
+ const outputPath = isCustomOutput ? path11.isAbsolute(rawOutputPath) ? rawOutputPath : path11.resolve(config.workspace, rawOutputPath) : config.dest.base;
1014
+ const jsDir = isCustomOutput ? "" : isProd ? config.paths.target.js : config.paths.tmp.js;
1015
+ const imgDir = isProd ? config.paths.target.img : config.paths.tmp.img;
1016
+ const fontDir = isProd ? config.paths.target.font : config.paths.tmp.font;
1017
+ const defaultWebpackConfig = {
1018
+ mode: isProd ? "production" : "development",
1019
+ context: config.workspace,
1020
+ entry,
1021
+ output: {
1022
+ path: outputPath,
1023
+ filename: isCustomOutput ? "[name].js" : isProd && config.assets.cache ? `${jsDir}/[name].[contenthash:8].js` : `${jsDir}/[name].js`,
1024
+ chunkFilename: isCustomOutput ? "chunk/[name].js" : isProd && config.assets.cache ? `${jsDir}/chunk/[name].[chunkhash:8].js` : `${jsDir}/chunk/[name].js`,
1025
+ publicPath: config.assets.virtualDir ? `/${config.assets.virtualDir}/` : "/",
1026
+ ...config.scripts.library ? { library: config.scripts.library } : {}
1027
+ },
1028
+ target: config.scripts.target || "web",
1029
+ externals: config.scripts.externals,
1030
+ plugins: [
1031
+ ...config.scripts.plugins || []
1032
+ ],
1033
+ resolve: {
1034
+ extensions: [
1035
+ ".wasm",
1036
+ ".mjs",
1037
+ ".js",
1038
+ ".jsx",
1039
+ ".ts",
1040
+ ".tsx",
1041
+ ".vue",
1042
+ ".json",
1043
+ ...config.scripts.extensions || []
1044
+ ],
1045
+ alias: {
1046
+ ...config.alias,
1047
+ ...config.scripts.alias
1048
+ }
1049
+ },
1050
+ module: {
1051
+ rules: [
1052
+ ...config.scripts.loaders || [],
1053
+ {
1054
+ test: /\.(png|jpe?g|gif|svg|webp)$/i,
1055
+ type: "asset/resource",
1056
+ generator: {
1057
+ filename: `${imgDir}/[name].[hash:8][ext]`
1058
+ }
1059
+ },
1060
+ {
1061
+ test: /\.(woff2?|eot|ttf|otf)$/i,
1062
+ type: "asset/resource",
1063
+ generator: {
1064
+ filename: `${fontDir}/[name].[hash:8][ext]`
1065
+ }
1066
+ }
1067
+ ]
1068
+ },
1069
+ optimization: {
1070
+ minimize: isProd || config.scripts.minify
1071
+ },
1072
+ devtool: isProd ? false : "eval-cheap-module-source-map"
1073
+ };
1074
+ const webpackConfig = {
1075
+ ...defaultWebpackConfig,
1076
+ ...config.scripts.webpackOptions,
1077
+ ...customOptions,
1078
+ output: {
1079
+ ...defaultWebpackConfig.output,
1080
+ ...config.scripts.webpackOptions?.output,
1081
+ ...customOptions?.output,
1082
+ path: outputPath
1083
+ }
1084
+ };
1085
+ return new Promise((resolve, reject) => {
1086
+ const compiler = webpack(webpackConfig);
1087
+ compiler.run((err, stats) => {
1088
+ if (err) {
1089
+ logger.error("webpack", err.stack || err);
1090
+ return reject(err);
1091
+ }
1092
+ if (stats?.hasErrors()) {
1093
+ const info = stats.toJson();
1094
+ logger.error("webpack", info.errors?.map((e) => e.message).join("\n"));
1095
+ return reject(new Error("Webpack compilation errors"));
1096
+ }
1097
+ resolve(stats);
1098
+ });
1099
+ });
1100
+ }
1101
+
1102
+ // src/bundler/rollup.ts
1103
+ import { rollup } from "rollup";
1104
+ import path12 from "path";
1105
+ async function runRollup(config, customInputOptions = {}, customOutputOptions = {}) {
1106
+ const isProd = config.env.isProd;
1107
+ let input = customInputOptions.input || config.scripts.entry;
1108
+ if (!input) {
1109
+ input = path12.join(config.src.js, "main.js");
1110
+ }
1111
+ const inputOptions = {
1112
+ ...config.scripts.rollupOptions,
1113
+ ...customInputOptions,
1114
+ input: customInputOptions.input || input
1115
+ };
1116
+ const outputOptions = customOutputOptions.file ? {
1117
+ format: "es",
1118
+ sourcemap: !isProd,
1119
+ ...customOutputOptions
1120
+ } : {
1121
+ dir: config.dest.js,
1122
+ format: "es",
1123
+ sourcemap: !isProd,
1124
+ ...customOutputOptions
1125
+ };
1126
+ try {
1127
+ const bundle = await rollup(inputOptions);
1128
+ await bundle.write(outputOptions);
1129
+ await bundle.close();
1130
+ } catch (err) {
1131
+ logger.error("rollup", err.message || err);
1132
+ throw err;
1133
+ }
1134
+ }
1135
+
1136
+ // src/bundler/esbuild.ts
1137
+ import * as esbuild from "esbuild";
1138
+ import path13 from "path";
1139
+ async function runEsbuild(config, customOptions = {}) {
1140
+ const isProd = config.env.isProd;
1141
+ let entryPoints = customOptions.entryPoints || config.scripts.entry;
1142
+ if (!entryPoints) {
1143
+ entryPoints = [path13.join(config.src.js, "main.js")];
1144
+ }
1145
+ const defaultOptions = {
1146
+ entryPoints: Array.isArray(entryPoints) ? entryPoints : typeof entryPoints === "string" ? [entryPoints] : entryPoints,
1147
+ absWorkingDir: config.workspace,
1148
+ bundle: true,
1149
+ outdir: config.dest.js,
1150
+ alias: {
1151
+ ...config.alias,
1152
+ ...config.scripts.alias
1153
+ },
1154
+ minify: isProd || config.scripts.minify,
1155
+ sourcemap: !isProd,
1156
+ target: config.scripts.target && config.scripts.target !== "web" ? config.scripts.target : "es2020",
1157
+ format: "esm",
1158
+ ...config.scripts.esbuildOptions,
1159
+ ...customOptions
1160
+ };
1161
+ try {
1162
+ return await esbuild.build(defaultOptions);
1163
+ } catch (err) {
1164
+ logger.error("esbuild", err.message || err);
1165
+ throw err;
1166
+ }
1167
+ }
1168
+
1169
+ // src/tasks/script.ts
1170
+ var ScriptTask = class extends BaseTask {
1171
+ constructor() {
1172
+ super({ name: "script", description: "Bundle JavaScript/TypeScript" });
1173
+ }
1174
+ async run(config) {
1175
+ const bundler = config.scripts.bundler;
1176
+ switch (bundler) {
1177
+ case "rollup" /* rollup */:
1178
+ await runRollup(config);
1179
+ break;
1180
+ case "esbuild" /* esbuild */:
1181
+ await runEsbuild(config);
1182
+ break;
1183
+ case "webpack" /* webpack */:
1184
+ default:
1185
+ await runWebpack(config);
1186
+ break;
1187
+ }
1188
+ }
1189
+ };
1190
+
1191
+ // src/tasks/html.ts
1192
+ import path14 from "path";
1193
+ import fg3 from "fast-glob";
1194
+
1195
+ // src/pipeline/transforms/htmlmin.ts
1196
+ import { minify } from "html-minifier-terser";
1197
+ function transformHtmlmin(options = {}) {
1198
+ return async (file2) => {
1199
+ if (!/\.html$/i.test(file2.extname)) return file2;
1200
+ try {
1201
+ const minified = await minify(file2.toString(), options);
1202
+ file2.contents = Buffer.from(minified);
1203
+ return file2;
1204
+ } catch (err) {
1205
+ logger.error("htmlmin", `Error minifying ${file2.path}: ${err.message}`);
1206
+ throw err;
1207
+ }
1208
+ };
1209
+ }
1210
+
1211
+ // src/pipeline/transforms/replace.ts
1212
+ function transformReplace(options, replacement) {
1213
+ let rules = [];
1214
+ if (Array.isArray(options)) {
1215
+ rules = options;
1216
+ } else if (typeof options === "object" && "substr" in options) {
1217
+ rules = [options];
1218
+ } else if (typeof options === "string" || options instanceof RegExp) {
1219
+ rules = [{ substr: options, replacement: replacement || "" }];
1220
+ }
1221
+ return async (file2) => {
1222
+ let content = file2.toString();
1223
+ for (const rule of rules) {
1224
+ if (typeof rule.replacement === "function") {
1225
+ content = content.replace(rule.substr, rule.replacement);
1226
+ } else {
1227
+ content = content.replace(rule.substr, rule.replacement);
1228
+ }
1229
+ }
1230
+ file2.contents = Buffer.from(content);
1231
+ return file2;
1232
+ };
1233
+ }
1234
+
1235
+ // src/tasks/html.ts
1236
+ var LIVE_RELOAD_SCRIPT = `
1237
+ <!-- BalmJS Live Reload -->
1238
+ <script>
1239
+ (() => {
1240
+ const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
1241
+ const ws = new WebSocket(\`\${protocol}//\${location.host}/__balm_ws\`);
1242
+ ws.onmessage = (e) => {
1243
+ try {
1244
+ const msg = JSON.parse(e.data);
1245
+ if (msg.type === 'reload') {
1246
+ console.log('[BalmJS] Reloading page...');
1247
+ location.reload();
1248
+ } else if (msg.type === 'reload-css') {
1249
+ console.log('[BalmJS] Reloading CSS...');
1250
+ const links = document.querySelectorAll('link[rel="stylesheet"]');
1251
+ for (const link of links) {
1252
+ const url = new URL(link.href);
1253
+ url.searchParams.set('_balm_t', Date.now().toString());
1254
+ link.href = url.href;
1255
+ }
1256
+ }
1257
+ } catch {}
1258
+ };
1259
+ ws.onclose = () => {
1260
+ setTimeout(() => location.reload(), 2000);
1261
+ };
1262
+ })();
1263
+ </script>
1264
+ `;
1265
+ var HtmlTask = class extends BaseTask {
1266
+ constructor() {
1267
+ super({ name: "html", description: "Process HTML templates" });
1268
+ }
1269
+ async run(config) {
1270
+ const customTemplate = config.scripts.htmlPluginOptions?.template;
1271
+ let pipeline;
1272
+ if (customTemplate) {
1273
+ const templatePath = path14.isAbsolute(customTemplate) ? customTemplate : path14.join(config.workspace, customTemplate);
1274
+ pipeline = Pipeline.from(templatePath, {
1275
+ cwd: config.workspace,
1276
+ base: path14.dirname(templatePath)
1277
+ });
1278
+ } else {
1279
+ const templateDir = config.scripts.injectHtml ? config.src.base : config.src.html;
1280
+ const pattern = path14.join(templateDir, "*.html");
1281
+ pipeline = Pipeline.from(pattern, {
1282
+ cwd: config.workspace,
1283
+ base: templateDir
1284
+ });
1285
+ }
1286
+ const publicUrlReplacement = config.assets.virtualDir ? `/${config.assets.virtualDir}` : "";
1287
+ pipeline.pipe(transformReplace(new RegExp(PUBLIC_URL, "g"), publicUrlReplacement));
1288
+ if (customTemplate) {
1289
+ pipeline.pipe(async (file2) => {
1290
+ file2.basename = "index.html";
1291
+ return file2;
1292
+ });
1293
+ }
1294
+ if (config.env.isProd) {
1295
+ pipeline.pipe(async (file2) => {
1296
+ if (/\.html$/i.test(file2.extname)) {
1297
+ let content = file2.toString();
1298
+ if (config.paths.source.css && config.paths.target.css && config.paths.source.css !== config.paths.target.css) {
1299
+ content = content.replaceAll(
1300
+ `/${config.paths.source.css}/`,
1301
+ `/${config.paths.target.css}/`
1302
+ );
1303
+ content = content.replaceAll(
1304
+ `"${config.paths.source.css}/`,
1305
+ `"${config.paths.target.css}/`
1306
+ );
1307
+ content = content.replaceAll(
1308
+ `'${config.paths.source.css}/`,
1309
+ `'${config.paths.target.css}/`
1310
+ );
1311
+ }
1312
+ if (config.paths.source.js && config.paths.target.js && config.paths.source.js !== config.paths.target.js) {
1313
+ content = content.replaceAll(
1314
+ `/${config.paths.source.js}/`,
1315
+ `/${config.paths.target.js}/`
1316
+ );
1317
+ content = content.replaceAll(
1318
+ `"${config.paths.source.js}/`,
1319
+ `"${config.paths.target.js}/`
1320
+ );
1321
+ content = content.replaceAll(
1322
+ `'${config.paths.source.js}/`,
1323
+ `'${config.paths.target.js}/`
1324
+ );
1325
+ }
1326
+ if (config.paths.source.img && config.paths.target.img && config.paths.source.img !== config.paths.target.img) {
1327
+ content = content.replaceAll(
1328
+ `/${config.paths.source.img}/`,
1329
+ `/${config.paths.target.img}/`
1330
+ );
1331
+ content = content.replaceAll(
1332
+ `"${config.paths.source.img}/`,
1333
+ `"${config.paths.target.img}/`
1334
+ );
1335
+ content = content.replaceAll(
1336
+ `'${config.paths.source.img}/`,
1337
+ `'${config.paths.target.img}/`
1338
+ );
1339
+ }
1340
+ if (config.paths.source.font && config.paths.target.font && config.paths.source.font !== config.paths.target.font) {
1341
+ content = content.replaceAll(
1342
+ `/${config.paths.source.font}/`,
1343
+ `/${config.paths.target.font}/`
1344
+ );
1345
+ content = content.replaceAll(
1346
+ `"${config.paths.source.font}/`,
1347
+ `"${config.paths.target.font}/`
1348
+ );
1349
+ content = content.replaceAll(
1350
+ `'${config.paths.source.font}/`,
1351
+ `'${config.paths.target.font}/`
1352
+ );
1353
+ }
1354
+ file2.contents = Buffer.from(content);
1355
+ }
1356
+ return file2;
1357
+ });
1358
+ }
1359
+ if (config.scripts.injectHtml) {
1360
+ pipeline.pipe(async (file2) => {
1361
+ if (/\.html$/i.test(file2.extname)) {
1362
+ let content = file2.toString();
1363
+ const entries = typeof config.scripts.entry === "object" && !Array.isArray(config.scripts.entry) ? Object.keys(config.scripts.entry) : ["main"];
1364
+ const jsDir = config.env.isProd ? config.paths.target.js : config.paths.tmp.js;
1365
+ const jsPathPrefix = config.assets.virtualDir ? `/${config.assets.virtualDir}/${jsDir}` : `/${jsDir}`;
1366
+ const destJsFolder = config.dest.js;
1367
+ const scriptTags = entries.filter(
1368
+ (entryName) => !new RegExp(`<script[^>]+src=["'][^"']*${entryName}[^"']*\\.js["']`, "i").test(
1369
+ content
1370
+ )
1371
+ ).map((entryName) => {
1372
+ let actualFilename = `${entryName}.js`;
1373
+ try {
1374
+ const matched = fg3.sync(`${entryName}.*.js`, { cwd: destJsFolder });
1375
+ if (matched.length) {
1376
+ const valid = matched.find((f) => !f.endsWith(".map") && !f.endsWith(".txt"));
1377
+ if (valid) {
1378
+ actualFilename = valid;
1379
+ }
1380
+ }
1381
+ } catch {
1382
+ }
1383
+ return `<script defer src="${jsPathPrefix}/${actualFilename}"></script>`;
1384
+ }).join("\n");
1385
+ if (scriptTags) {
1386
+ if (content.includes("</body>")) {
1387
+ content = content.replace("</body>", `${scriptTags}
1388
+ </body>`);
1389
+ } else {
1390
+ content += `
1391
+ ${scriptTags}`;
1392
+ }
1393
+ file2.contents = Buffer.from(content);
1394
+ }
1395
+ }
1396
+ return file2;
1397
+ });
1398
+ }
1399
+ if (config.env.isProd) {
1400
+ pipeline.pipe(transformHtmlmin(config.html.options));
1401
+ } else {
1402
+ pipeline.pipe(async (file2) => {
1403
+ if (/\.html$/i.test(file2.extname)) {
1404
+ let content = file2.toString();
1405
+ if (content.includes("</body>")) {
1406
+ content = content.replace("</body>", `${LIVE_RELOAD_SCRIPT}
1407
+ </body>`);
1408
+ } else {
1409
+ content += `
1410
+ ${LIVE_RELOAD_SCRIPT}`;
1411
+ }
1412
+ file2.contents = Buffer.from(content);
1413
+ }
1414
+ return file2;
1415
+ });
1416
+ }
1417
+ await pipeline.dest(config.dest.html);
1418
+ }
1419
+ };
1420
+
1421
+ // src/tasks/copy.ts
1422
+ import path16 from "path";
1423
+
1424
+ // src/pipeline/transforms/rename.ts
1425
+ import path15 from "path";
1426
+ function transformRename(options = {}) {
1427
+ return async (file2) => {
1428
+ if (typeof options === "string") {
1429
+ file2.path = path15.resolve(path15.dirname(file2.path), options);
1430
+ return file2;
1431
+ }
1432
+ if (typeof options === "function") {
1433
+ const parsed = {
1434
+ dirname: path15.dirname(file2.path),
1435
+ basename: file2.stem,
1436
+ extname: file2.extname
1437
+ };
1438
+ const result = options(parsed);
1439
+ if (result) {
1440
+ if (result.dirname) file2.path = path15.join(result.dirname, path15.basename(file2.path));
1441
+ if (result.basename) file2.stem = result.basename;
1442
+ if (result.extname) file2.extname = result.extname;
1443
+ }
1444
+ return file2;
1445
+ }
1446
+ const { dirname, prefix = "", basename, suffix = "", extname } = options;
1447
+ const currentDir = dirname ? path15.resolve(file2.cwd, dirname) : path15.dirname(file2.path);
1448
+ const base = basename !== void 0 ? basename : file2.stem;
1449
+ const ext = extname !== void 0 ? extname : file2.extname;
1450
+ file2.path = path15.join(currentDir, `${prefix}${base}${suffix}${ext.startsWith(".") ? ext : `.${ext}`}`);
1451
+ return file2;
1452
+ };
1453
+ }
1454
+
1455
+ // src/tasks/copy.ts
1456
+ var CopyTask = class extends BaseTask {
1457
+ options;
1458
+ constructor(options) {
1459
+ super({ name: "copy", description: "Copy files to destination" });
1460
+ this.options = options;
1461
+ }
1462
+ async run(config) {
1463
+ const pipeline = Pipeline.from(this.options.input, {
1464
+ cwd: config.workspace
1465
+ });
1466
+ if (this.options.rename) {
1467
+ pipeline.pipe(transformRename(this.options.rename));
1468
+ }
1469
+ const outDir = path16.isAbsolute(this.options.output) ? this.options.output : path16.join(config.workspace, this.options.output);
1470
+ await pipeline.dest(outDir);
1471
+ }
1472
+ };
1473
+
1474
+ // src/tasks/remove.ts
1475
+ import path17 from "path";
1476
+ var RemoveTask = class extends BaseTask {
1477
+ target;
1478
+ constructor(target) {
1479
+ super({ name: "remove", description: "Remove files and directories" });
1480
+ this.target = target;
1481
+ }
1482
+ async run(config) {
1483
+ const targets = Array.isArray(this.target) ? this.target : [this.target];
1484
+ const resolvedTargets = targets.map(
1485
+ (t) => path17.isAbsolute(t) ? t : path17.join(config.workspace, t)
1486
+ );
1487
+ const deleted = await file.remove(resolvedTargets);
1488
+ logger.warn("remove", `Removed: ${deleted.join(", ")}`);
1489
+ }
1490
+ };
1491
+
1492
+ // src/tasks/replace.ts
1493
+ import path18 from "path";
1494
+ var ReplaceTask = class extends BaseTask {
1495
+ taskOptions;
1496
+ constructor(taskOptions) {
1497
+ super({ name: "replace", description: "Replace content in files" });
1498
+ this.taskOptions = taskOptions;
1499
+ }
1500
+ async run(config) {
1501
+ const pipeline = Pipeline.from(this.taskOptions.input, {
1502
+ cwd: config.workspace
1503
+ });
1504
+ pipeline.pipe(transformReplace(this.taskOptions.options));
1505
+ const outDir = path18.isAbsolute(this.taskOptions.output) ? this.taskOptions.output : path18.join(config.workspace, this.taskOptions.output);
1506
+ await pipeline.dest(outDir);
1507
+ }
1508
+ };
1509
+
1510
+ // src/tasks/cache.ts
1511
+ import fg4 from "fast-glob";
1512
+ import fs4 from "fs/promises";
1513
+
1514
+ // src/pipeline/transforms/cache.ts
1515
+ import path19 from "path";
1516
+ import picomatch from "picomatch";
1517
+ function matchesAny(filePath, rules) {
1518
+ if (!rules || !rules.length) return false;
1519
+ const normalized = filePath.replace(/\\/g, "/");
1520
+ const baseName = path19.basename(normalized);
1521
+ for (const rawRule of rules) {
1522
+ if (typeof rawRule === "string") {
1523
+ const rule = rawRule.replace(/\\/g, "/");
1524
+ const cleanRule = rule.replace(/^dist\//, "").replace(/^\.\/dist\//, "");
1525
+ if (normalized.endsWith(rule) || normalized.endsWith(cleanRule) || baseName === rule || baseName === cleanRule || baseName.endsWith(rule) || picomatch.isMatch(normalized, rule) || picomatch.isMatch(normalized, cleanRule) || picomatch.isMatch(normalized, `**/${cleanRule}`) || picomatch.isMatch(baseName, rule) || picomatch.isMatch(baseName, cleanRule)) {
1526
+ return true;
1527
+ }
1528
+ } else if (rawRule instanceof RegExp) {
1529
+ if (rawRule.test(normalized) || rawRule.test(baseName)) return true;
1530
+ }
1531
+ }
1532
+ return false;
1533
+ }
1534
+ var AssetRevisioner = class {
1535
+ options;
1536
+ manifest = {};
1537
+ constructor(options = {}) {
1538
+ const baseDontRename = [
1539
+ ".html",
1540
+ "favicon.ico",
1541
+ "manifest.json",
1542
+ "robots.txt",
1543
+ "*.ico",
1544
+ "workbox-sw.js",
1545
+ "workbox-sw.js.map",
1546
+ "service-worker.js",
1547
+ "sw.js"
1548
+ ];
1549
+ this.options = {
1550
+ fileNameManifest: "rev-manifest.json",
1551
+ dontRenameFile: baseDontRename,
1552
+ dontUpdateReference: [],
1553
+ hashLength: 8,
1554
+ ...options
1555
+ };
1556
+ if (options.dontRenameFile) {
1557
+ this.options.dontRenameFile = [
1558
+ ...baseDontRename,
1559
+ ...options.dontRenameFile
1560
+ ];
1561
+ }
1562
+ }
1563
+ process(files) {
1564
+ const hashLength = this.options.hashLength || 8;
1565
+ const outputFiles = [];
1566
+ for (const file2 of files) {
1567
+ const origRelative = file2.relative;
1568
+ file2.revPathOriginal = file2.path;
1569
+ const alreadyHashed = /\.[a-f0-9]{8}$/i.test(file2.stem);
1570
+ const shouldRename = !alreadyHashed && !matchesAny(file2.path, this.options.dontRenameFile) && !matchesAny(file2.relative, this.options.dontRenameFile);
1571
+ if (shouldRename) {
1572
+ const fileHash = file.hash(file2.contents, hashLength);
1573
+ const dir = path19.dirname(file2.path);
1574
+ const newFilename = `${file2.stem}.${fileHash}${file2.extname}`;
1575
+ file2.path = path19.join(dir, newFilename);
1576
+ }
1577
+ if (alreadyHashed) {
1578
+ const unhashedRelative = file2.relative.replace(/\.[a-f0-9]{8}(\.[^.]+)$/i, "$1");
1579
+ this.manifest[unhashedRelative] = file2.relative;
1580
+ }
1581
+ this.manifest[origRelative] = file2.relative;
1582
+ outputFiles.push(file2);
1583
+ }
1584
+ const manifestEntries = Object.entries(this.manifest).filter(([orig, reved]) => orig !== reved).sort((a, b) => b[0].length - a[0].length);
1585
+ for (const file2 of outputFiles) {
1586
+ const shouldUpdate = !matchesAny(file2.path, this.options.dontUpdateReference) && !matchesAny(file2.relative, this.options.dontUpdateReference);
1587
+ if (shouldUpdate && /\.(html|css|js)$/i.test(file2.extname)) {
1588
+ let content = file2.toString();
1589
+ for (const [orig, reved] of manifestEntries) {
1590
+ content = content.replaceAll(orig, reved);
1591
+ const origBase = path19.basename(orig);
1592
+ const revedBase = path19.basename(reved);
1593
+ const escapedBase = origBase.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1594
+ const baseRegex = new RegExp(`${escapedBase}(?![a-zA-Z0-9_])`, "g");
1595
+ content = content.replace(baseRegex, revedBase);
1596
+ }
1597
+ file2.contents = Buffer.from(content);
1598
+ }
1599
+ }
1600
+ const manifestName = this.options.fileNameManifest || "rev-manifest.json";
1601
+ const firstBase = files[0]?.base || process.cwd();
1602
+ const manifestFile = new VirtualFile({
1603
+ cwd: files[0]?.cwd || process.cwd(),
1604
+ base: firstBase,
1605
+ path: path19.join(firstBase, manifestName),
1606
+ contents: JSON.stringify(this.manifest, null, 2)
1607
+ });
1608
+ outputFiles.push(manifestFile);
1609
+ return outputFiles;
1610
+ }
1611
+ };
1612
+
1613
+ // src/tasks/cache.ts
1614
+ var CacheTask = class extends BaseTask {
1615
+ constructor() {
1616
+ super({ name: "cache", description: "Revision assets and generate manifest" });
1617
+ }
1618
+ async run(config) {
1619
+ const destDir = config.dest.base;
1620
+ const filesToRev = await fg4(["**/*"], {
1621
+ cwd: destDir,
1622
+ absolute: true,
1623
+ onlyFiles: true
1624
+ });
1625
+ if (!filesToRev.length) {
1626
+ logger.warn("cache", "No files found to cache");
1627
+ return;
1628
+ }
1629
+ const virtualFiles = await Promise.all(
1630
+ filesToRev.map(async (filePath) => {
1631
+ const buffer = await fs4.readFile(filePath);
1632
+ return new VirtualFile({
1633
+ cwd: config.workspace,
1634
+ base: destDir,
1635
+ path: filePath,
1636
+ contents: buffer
1637
+ });
1638
+ })
1639
+ );
1640
+ const cacheOptions = {
1641
+ ...config.assets.options,
1642
+ dontRenameFile: [
1643
+ ".html",
1644
+ "favicon.ico",
1645
+ "manifest.json",
1646
+ "robots.txt",
1647
+ "*.ico",
1648
+ "icons/*",
1649
+ "**/icons/**",
1650
+ ...config.assets.excludes || [],
1651
+ ...config.assets.options?.dontRenameFile || []
1652
+ ]
1653
+ };
1654
+ const revisioner = new AssetRevisioner(cacheOptions);
1655
+ const resultFiles = revisioner.process(virtualFiles);
1656
+ for (const vFile of resultFiles) {
1657
+ if (vFile.revPathOriginal && vFile.revPathOriginal !== vFile.path) {
1658
+ await file.remove(vFile.revPathOriginal);
1659
+ }
1660
+ }
1661
+ for (const vFile of resultFiles) {
1662
+ await file.writeFile(vFile.path, vFile.contents);
1663
+ }
1664
+ logger.success("cache", "Asset revisioning and rev-manifest.json completed");
1665
+ }
1666
+ };
1667
+
1668
+ // src/tasks/publish.ts
1669
+ import path20 from "path";
1670
+ var PublishTask = class extends BaseTask {
1671
+ options;
1672
+ constructor(options = {}) {
1673
+ super({ name: "publish", description: "Publish assets or templates" });
1674
+ this.options = options;
1675
+ }
1676
+ async run(config) {
1677
+ if (!config.env.isProd) {
1678
+ logger.warn("publish", "`mix.publish()` is only supported in production mode");
1679
+ return;
1680
+ }
1681
+ const { input, output, renameOptions } = this.options;
1682
+ if (Array.isArray(input)) {
1683
+ for (const item of input) {
1684
+ await this.publishItem(config, item.input, item.output, item.renameOptions);
1685
+ }
1686
+ } else if (typeof input === "string" && output) {
1687
+ await this.publishItem(config, input, output, renameOptions);
1688
+ } else {
1689
+ const srcDir = config.dest.base;
1690
+ const targetDir = config.assets.static || path20.join(config.assets.root, config.assets.mainDir);
1691
+ const pipeline = Pipeline.from(["**/*"], {
1692
+ cwd: srcDir,
1693
+ base: srcDir
1694
+ });
1695
+ await pipeline.dest(path20.resolve(config.workspace, targetDir));
1696
+ }
1697
+ }
1698
+ async publishItem(config, input, output, rename) {
1699
+ const srcPath = path20.isAbsolute(input) ? input : path20.join(config.dest.base, input);
1700
+ const destDir = path20.isAbsolute(output) ? output : path20.join(config.workspace, config.assets.root, output);
1701
+ const pipeline = Pipeline.from(srcPath, {
1702
+ cwd: config.dest.base
1703
+ });
1704
+ if (rename) {
1705
+ pipeline.pipe(transformRename(rename));
1706
+ }
1707
+ await pipeline.dest(destDir);
1708
+ }
1709
+ };
1710
+
1711
+ // src/tasks/pwa.ts
1712
+ import path21 from "path";
1713
+ import fs5 from "fs/promises";
1714
+ import { existsSync as existsSync2 } from "fs";
1715
+ import { createRequire } from "module";
1716
+ import { generateSW, injectManifest } from "workbox-build";
1717
+ var PwaTask = class extends BaseTask {
1718
+ options;
1719
+ constructor(options = {}) {
1720
+ super({ name: "pwa", description: "Generate Progressive Web App Service Worker" });
1721
+ this.options = options;
1722
+ }
1723
+ async run(config) {
1724
+ const globDirectory = config.dest.base;
1725
+ const swDest = path21.join(globDirectory, config.pwa.swDestFilename);
1726
+ const swSrc = path21.join(config.src.base, config.pwa.swSrcFilename);
1727
+ const require2 = createRequire(import.meta.url);
1728
+ const candidatePaths = [
1729
+ path21.join(config.workspace, "node_modules/workbox-sw/build/workbox-sw.js"),
1730
+ path21.join(config.workspace, "node_modules/workbox-sw/controllers/WorkboxSW.mjs")
1731
+ ];
1732
+ try {
1733
+ const resolved = require2.resolve("workbox-sw/build/workbox-sw.js");
1734
+ candidatePaths.unshift(resolved);
1735
+ } catch {
1736
+ }
1737
+ try {
1738
+ const resolvedPkg = require2.resolve("workbox-sw");
1739
+ candidatePaths.push(path21.join(path21.dirname(resolvedPkg), "build/workbox-sw.js"));
1740
+ } catch {
1741
+ }
1742
+ for (const p of candidatePaths) {
1743
+ if (existsSync2(p)) {
1744
+ const destFile = path21.join(globDirectory, "workbox-sw.js");
1745
+ await fs5.copyFile(p, destFile);
1746
+ const mapFile = `${p}.map`;
1747
+ if (existsSync2(mapFile)) {
1748
+ await fs5.copyFile(mapFile, path21.join(globDirectory, "workbox-sw.js.map"));
1749
+ }
1750
+ break;
1751
+ }
1752
+ }
1753
+ const mode = this.options.mode || config.pwa.mode || "generateSW";
1754
+ const mergedOptions = {
1755
+ globDirectory,
1756
+ swDest,
1757
+ ...config.pwa.options,
1758
+ ...this.options.options
1759
+ };
1760
+ if (mode === "injectManifest") {
1761
+ mergedOptions.swSrc = mergedOptions.swSrc || swSrc;
1762
+ }
1763
+ try {
1764
+ if (mode === "generateSW") {
1765
+ const { count, size } = await generateSW(mergedOptions);
1766
+ logger.info("pwa", `Generated '${swDest}', precaching ${count} files (${size} bytes)`);
1767
+ } else {
1768
+ const { count, size } = await injectManifest(mergedOptions);
1769
+ logger.info("pwa", `Injected manifest into '${swDest}', precaching ${count} files (${size} bytes)`);
1770
+ }
1771
+ if (existsSync2(swDest) && config.pwa.version) {
1772
+ let swContent = await fs5.readFile(swDest, "utf-8");
1773
+ swContent = swContent.replaceAll("{{ version }}", config.pwa.version).replaceAll("{{version}}", config.pwa.version);
1774
+ await fs5.writeFile(swDest, swContent, "utf-8");
1775
+ }
1776
+ const swJsDest = path21.join(globDirectory, "sw.js");
1777
+ const serviceWorkerJsDest = path21.join(globDirectory, "service-worker.js");
1778
+ if (existsSync2(serviceWorkerJsDest) && !existsSync2(swJsDest)) {
1779
+ await fs5.copyFile(serviceWorkerJsDest, swJsDest);
1780
+ } else if (existsSync2(swJsDest) && !existsSync2(serviceWorkerJsDest)) {
1781
+ await fs5.copyFile(swJsDest, serviceWorkerJsDest);
1782
+ }
1783
+ } catch (err) {
1784
+ logger.warn("pwa", `Service worker generation failed: ${err.message || err}`);
1785
+ }
1786
+ }
1787
+ };
1788
+
1789
+ // src/tasks/zip.ts
1790
+ import fs6 from "fs";
1791
+ import path22 from "path";
1792
+ import archiver from "archiver";
1793
+ var ZipTask = class extends BaseTask {
1794
+ options;
1795
+ constructor(options = {}) {
1796
+ super({ name: "zip", description: "Compress files into archive" });
1797
+ this.options = options;
1798
+ }
1799
+ async run(config) {
1800
+ const input = this.options.input || config.dest.base;
1801
+ const outputDir = this.options.output ? path22.isAbsolute(this.options.output) ? this.options.output : path22.join(config.workspace, this.options.output) : config.workspace;
1802
+ const filename = this.options.filename || "archive.zip";
1803
+ const zipPath = path22.join(outputDir, filename);
1804
+ await file.ensureDir(outputDir);
1805
+ return new Promise((resolve, reject) => {
1806
+ const outputStream = fs6.createWriteStream(zipPath);
1807
+ const archive = archiver("zip", { zlib: { level: 9 } });
1808
+ outputStream.on("close", () => {
1809
+ logger.success("zip", `Created ${zipPath} (${archive.pointer()} total bytes)`);
1810
+ resolve();
1811
+ });
1812
+ archive.on("error", (err) => {
1813
+ logger.error("zip", err.message);
1814
+ reject(err);
1815
+ });
1816
+ archive.pipe(outputStream);
1817
+ const inputs = Array.isArray(input) ? input : [input];
1818
+ for (const item of inputs) {
1819
+ const fullPath = path22.isAbsolute(item) ? item : path22.join(config.workspace, item);
1820
+ if (fs6.existsSync(fullPath)) {
1821
+ const stat = fs6.statSync(fullPath);
1822
+ if (stat.isDirectory()) {
1823
+ archive.directory(fullPath, false);
1824
+ } else {
1825
+ archive.file(fullPath, { name: path22.basename(fullPath) });
1826
+ }
1827
+ }
1828
+ }
1829
+ archive.finalize();
1830
+ });
1831
+ }
1832
+ };
1833
+
1834
+ // src/tasks/ftp.ts
1835
+ import path23 from "path";
1836
+ import { Client } from "ssh2";
1837
+ import fg5 from "fast-glob";
1838
+ var FtpTask = class extends BaseTask {
1839
+ ftpOptions;
1840
+ constructor(ftpOptions = {}) {
1841
+ super({ name: "ftp", description: "Upload files via SFTP" });
1842
+ this.ftpOptions = ftpOptions;
1843
+ }
1844
+ async run(config) {
1845
+ const options = {
1846
+ port: 22,
1847
+ remotePath: "/",
1848
+ ...config.ftp.options,
1849
+ ...this.ftpOptions.options
1850
+ };
1851
+ if (!options.host) {
1852
+ logger.error("ftp", "`host` required for SFTP");
1853
+ return;
1854
+ }
1855
+ const localFiles = this.ftpOptions.localFiles || config.dest.base;
1856
+ const files = await fg5(localFiles, { cwd: config.workspace, absolute: true, onlyFiles: true });
1857
+ if (!files.length) {
1858
+ logger.warn("ftp", "No files to upload");
1859
+ return;
1860
+ }
1861
+ return new Promise((resolve, reject) => {
1862
+ const conn = new Client();
1863
+ conn.on("ready", () => {
1864
+ conn.sftp((err, sftp) => {
1865
+ if (err) {
1866
+ conn.end();
1867
+ return reject(err);
1868
+ }
1869
+ let pending = files.length;
1870
+ for (const file2 of files) {
1871
+ const rel = path23.relative(config.workspace, file2);
1872
+ const remoteFile = path23.posix.join(options.remotePath, rel);
1873
+ sftp.fastPut(file2, remoteFile, (putErr) => {
1874
+ if (putErr) {
1875
+ logger.error("ftp", `Failed uploading ${file2}: ${putErr.message}`);
1876
+ } else {
1877
+ logger.info("ftp", `Uploaded ${file2} -> ${remoteFile}`);
1878
+ }
1879
+ pending--;
1880
+ if (pending === 0) {
1881
+ conn.end();
1882
+ resolve();
1883
+ }
1884
+ });
1885
+ }
1886
+ });
1887
+ });
1888
+ conn.on("error", (connErr) => {
1889
+ logger.error("ftp", connErr.message);
1890
+ reject(connErr);
1891
+ });
1892
+ conn.connect(options);
1893
+ });
1894
+ }
1895
+ };
1896
+
1897
+ // src/tasks/server.ts
1898
+ import http from "http";
1899
+ import fs8 from "fs";
1900
+ import path24 from "path";
1901
+ import connect from "connect";
1902
+ import serveStatic from "serve-static";
1903
+ import { WebSocketServer, WebSocket } from "ws";
1904
+ import { createProxyMiddleware } from "http-proxy-middleware";
1905
+
1906
+ // src/watcher/index.ts
1907
+ import fs7 from "fs";
1908
+ import pc3 from "picocolors";
1909
+ var FileWatcher = class {
1910
+ config;
1911
+ broadcast;
1912
+ debounceTimer = null;
1913
+ pendingFiles = /* @__PURE__ */ new Set();
1914
+ styleTask;
1915
+ scriptTask;
1916
+ htmlTask;
1917
+ watcher = null;
1918
+ constructor(config, broadcast) {
1919
+ this.config = config;
1920
+ this.broadcast = broadcast;
1921
+ this.styleTask = new StyleTask();
1922
+ this.scriptTask = new ScriptTask();
1923
+ this.htmlTask = new HtmlTask();
1924
+ }
1925
+ start() {
1926
+ const watchDir = this.config.src.base;
1927
+ if (!fs7.existsSync(watchDir)) return;
1928
+ logger.info("watch", `Watching for file changes in ${pc3.cyan(watchDir)}`);
1929
+ try {
1930
+ this.watcher = fs7.watch(watchDir, { recursive: true }, (eventType, filename) => {
1931
+ if (!filename) return;
1932
+ this.pendingFiles.add(filename);
1933
+ if (this.debounceTimer) clearTimeout(this.debounceTimer);
1934
+ this.debounceTimer = setTimeout(() => {
1935
+ this.handleChanges();
1936
+ }, 50);
1937
+ });
1938
+ } catch (err) {
1939
+ logger.warn("watch", `Native recursive watch failed: ${err.message}`);
1940
+ }
1941
+ }
1942
+ async handleChanges() {
1943
+ const changed = Array.from(this.pendingFiles);
1944
+ this.pendingFiles.clear();
1945
+ const hasStyle = changed.some((f) => /\.(scss|sass|less|css)$/i.test(f));
1946
+ const hasScript = changed.some((f) => /\.(js|mjs|cjs|ts|jsx|tsx)$/i.test(f));
1947
+ const hasHtml = changed.some((f) => /\.html$/i.test(f));
1948
+ try {
1949
+ if (hasStyle) {
1950
+ const start = performance.now();
1951
+ await this.styleTask.run(this.config);
1952
+ const duration = (performance.now() - start).toFixed(1);
1953
+ logger.info("watch", `Recompiled styles in ${pc3.bold(duration + "ms")}`);
1954
+ this.broadcast({ type: "reload-css" });
1955
+ }
1956
+ if (hasScript) {
1957
+ const start = performance.now();
1958
+ await this.scriptTask.run(this.config);
1959
+ const duration = (performance.now() - start).toFixed(1);
1960
+ logger.info("watch", `Rebundled scripts in ${pc3.bold(duration + "ms")}`);
1961
+ this.broadcast({ type: "reload" });
1962
+ }
1963
+ if (hasHtml) {
1964
+ const start = performance.now();
1965
+ await this.htmlTask.run(this.config);
1966
+ const duration = (performance.now() - start).toFixed(1);
1967
+ logger.info("watch", `Recompiled HTML in ${pc3.bold(duration + "ms")}`);
1968
+ this.broadcast({ type: "reload" });
1969
+ }
1970
+ if (!hasStyle && !hasScript && !hasHtml && changed.length > 0) {
1971
+ this.broadcast({ type: "reload" });
1972
+ }
1973
+ } catch (err) {
1974
+ logger.error("watch", `Incremental rebuild failed: ${err?.message || err}`);
1975
+ }
1976
+ }
1977
+ stop() {
1978
+ if (this.watcher) {
1979
+ this.watcher.close();
1980
+ this.watcher = null;
1981
+ }
1982
+ }
1983
+ };
1984
+
1985
+ // src/tasks/server.ts
1986
+ import pc4 from "picocolors";
1987
+ var ServerTask = class extends BaseTask {
1988
+ watcher = null;
1989
+ wsServer = null;
1990
+ clients = /* @__PURE__ */ new Set();
1991
+ constructor() {
1992
+ super({ name: "serve", description: "Start development server with live reload" });
1993
+ }
1994
+ broadcast(data) {
1995
+ const payload = JSON.stringify(data);
1996
+ for (const client of this.clients) {
1997
+ if (client.readyState === WebSocket.OPEN) {
1998
+ client.send(payload);
1999
+ }
2000
+ }
2001
+ }
2002
+ async run(config) {
2003
+ if (config.env.isProd) {
2004
+ logger.warn("server", "Development server is disabled in production mode");
2005
+ return;
2006
+ }
2007
+ const app = connect();
2008
+ const port = config.server.port || 3e3;
2009
+ const host = config.server.host || "localhost";
2010
+ app.use((req, _res, next) => {
2011
+ if (req.url?.includes("%PUBLIC_URL%")) {
2012
+ req.url = req.url.replace("/%PUBLIC_URL%", "").replace("%PUBLIC_URL%", "") || "/";
2013
+ }
2014
+ next();
2015
+ });
2016
+ app.use((req, res, next) => {
2017
+ if (req.url === "/manifest.json" || req.url?.endsWith("/manifest.json")) {
2018
+ const candidates = [
2019
+ path24.join(config.dest.base, "manifest.json"),
2020
+ path24.join(config.src.base, "manifest.json"),
2021
+ path24.join(config.workspace, "manifest.json")
2022
+ ];
2023
+ const file2 = candidates.find((f) => fs8.existsSync(f));
2024
+ if (file2) {
2025
+ let content = fs8.readFileSync(file2, "utf-8");
2026
+ const replacement = config.assets.virtualDir ? `/${config.assets.virtualDir}` : "";
2027
+ content = content.replaceAll("%PUBLIC_URL%", replacement);
2028
+ res.setHeader("Content-Type", "application/json; charset=utf-8");
2029
+ return res.end(content);
2030
+ }
2031
+ }
2032
+ next();
2033
+ });
2034
+ if (config.paths.source.css) {
2035
+ app.use("/" + config.paths.source.css, serveStatic(config.dest.css));
2036
+ }
2037
+ if (config.paths.tmp.css && config.paths.tmp.css !== config.paths.source.css) {
2038
+ app.use("/" + config.paths.tmp.css, serveStatic(config.dest.css));
2039
+ }
2040
+ if (config.paths.source.js) {
2041
+ app.use("/" + config.paths.source.js, serveStatic(config.dest.js));
2042
+ }
2043
+ if (config.paths.tmp.js && config.paths.tmp.js !== config.paths.source.js) {
2044
+ app.use("/" + config.paths.tmp.js, serveStatic(config.dest.js));
2045
+ }
2046
+ app.use("/chunk", serveStatic(path24.join(config.dest.js, "chunk")));
2047
+ app.use("/chunk", serveStatic(path24.join(config.dest.base, "chunk")));
2048
+ if (config.paths.source.img) {
2049
+ app.use(
2050
+ "/" + config.paths.source.img,
2051
+ serveStatic(path24.join(config.src.base, config.paths.source.img))
2052
+ );
2053
+ }
2054
+ if (config.paths.tmp.img) {
2055
+ app.use("/" + config.paths.tmp.img, serveStatic(config.dest.img));
2056
+ }
2057
+ app.use("/img", serveStatic(path24.join(config.dest.base, "img")));
2058
+ app.use("/img", serveStatic(path24.join(config.dest.js, "img")));
2059
+ if (config.paths.source.font) {
2060
+ app.use(
2061
+ "/" + config.paths.source.font,
2062
+ serveStatic(path24.join(config.src.base, config.paths.source.font))
2063
+ );
2064
+ }
2065
+ if (config.paths.tmp.font) {
2066
+ app.use("/" + config.paths.tmp.font, serveStatic(config.dest.font));
2067
+ }
2068
+ app.use("/font", serveStatic(path24.join(config.dest.base, "font")));
2069
+ app.use("/font", serveStatic(path24.join(config.dest.js, "font")));
2070
+ app.use("/fonts", serveStatic(path24.join(config.src.base, "fonts")));
2071
+ app.use("/fonts", serveStatic(path24.join(config.dest.base, "fonts")));
2072
+ app.use(serveStatic(config.dest.base));
2073
+ app.use(serveStatic(config.src.base));
2074
+ app.use(serveStatic(config.workspace));
2075
+ if (config.server.historyOptions) {
2076
+ app.use((req, res, next) => {
2077
+ const isGetOrHead = req.method === "GET" || req.method === "HEAD";
2078
+ const isHtmlNav = !req.url?.includes(".") || req.headers.accept?.includes("text/html");
2079
+ if (isGetOrHead && isHtmlNav) {
2080
+ const indexPath = path24.join(config.dest.html, "index.html");
2081
+ const srcIndexPath = path24.join(config.src.base, "index.html");
2082
+ const fileToServe = fs8.existsSync(indexPath) ? indexPath : srcIndexPath;
2083
+ if (fs8.existsSync(fileToServe)) {
2084
+ res.setHeader("Content-Type", "text/html; charset=utf-8");
2085
+ if (req.method === "HEAD") {
2086
+ return res.end();
2087
+ }
2088
+ return fs8.createReadStream(fileToServe).pipe(res);
2089
+ }
2090
+ }
2091
+ next();
2092
+ });
2093
+ }
2094
+ if (config.server.proxy && typeof config.server.proxy === "object") {
2095
+ const proxyEntries = Array.isArray(config.server.proxy) ? config.server.proxy : [config.server.proxy];
2096
+ for (const entry of proxyEntries) {
2097
+ if (entry.context && entry.options) {
2098
+ app.use(createProxyMiddleware(entry.options));
2099
+ }
2100
+ }
2101
+ }
2102
+ const server = http.createServer(app);
2103
+ this.wsServer = new WebSocketServer({ server, path: "/__balm_ws" });
2104
+ this.wsServer.on("connection", (socket) => {
2105
+ this.clients.add(socket);
2106
+ socket.on("close", () => this.clients.delete(socket));
2107
+ });
2108
+ this.watcher = new FileWatcher(config, (data) => {
2109
+ this.broadcast(data);
2110
+ });
2111
+ this.watcher.start();
2112
+ return new Promise((resolve) => {
2113
+ server.listen(port, host, () => {
2114
+ console.log("");
2115
+ logger.success("server", pc4.bold("BalmJS Dev Server is ready!"));
2116
+ console.log(` ${pc4.green("\u279C")} ${pc4.bold("Local:")} ${pc4.cyan(`http://${host}:${port}/`)}`);
2117
+ console.log(` ${pc4.green("\u279C")} ${pc4.bold("Network:")} ${pc4.cyan(`http://127.0.0.1:${port}/`)}`);
2118
+ console.log("");
2119
+ if (config.env.isTest) {
2120
+ this.watcher?.stop();
2121
+ this.wsServer?.close();
2122
+ server.close();
2123
+ resolve();
2124
+ }
2125
+ });
2126
+ });
2127
+ }
2128
+ };
2129
+
2130
+ // src/tasks/static.ts
2131
+ import path25 from "path";
2132
+ import { existsSync as existsSync3 } from "fs";
2133
+ var StaticTask = class extends BaseTask {
2134
+ constructor() {
2135
+ super({ name: "static", description: "Copy static assets, images, fonts, and manifest" });
2136
+ }
2137
+ async run(config) {
2138
+ const srcBase = config.src.base;
2139
+ const destBase = config.dest.base;
2140
+ const staticPatterns = [
2141
+ path25.join(srcBase, "*.{ico,png,svg,json,txt,xml,webmanifest}"),
2142
+ `!${path25.join(srcBase, "*.html")}`
2143
+ ];
2144
+ const pipeline = Pipeline.from(staticPatterns, {
2145
+ cwd: config.workspace,
2146
+ base: srcBase
2147
+ });
2148
+ const publicUrlReplacement = config.assets.virtualDir ? `/${config.assets.virtualDir}` : "";
2149
+ pipeline.pipe(async (file2) => {
2150
+ if (file2.basename === "manifest.json" || file2.extname === ".json") {
2151
+ let content = file2.toString();
2152
+ content = content.replaceAll(PUBLIC_URL, publicUrlReplacement);
2153
+ file2.contents = Buffer.from(content);
2154
+ }
2155
+ return file2;
2156
+ });
2157
+ await pipeline.dest(destBase);
2158
+ if (config.src.img && existsSync3(config.src.img)) {
2159
+ const imgPipeline = Pipeline.from(path25.join(config.src.img, "**/*"), {
2160
+ cwd: config.workspace,
2161
+ base: config.src.img
2162
+ });
2163
+ await imgPipeline.dest(config.dest.img);
2164
+ if (config.paths.source.img && config.paths.target.img && config.paths.source.img !== config.paths.target.img) {
2165
+ const compatPipeline = Pipeline.from(path25.join(config.src.img, "**/*"), {
2166
+ cwd: config.workspace,
2167
+ base: config.src.img
2168
+ });
2169
+ await compatPipeline.dest(path25.join(destBase, config.paths.source.img));
2170
+ }
2171
+ }
2172
+ if (config.src.font && existsSync3(config.src.font)) {
2173
+ const fontPipeline = Pipeline.from(path25.join(config.src.font, "**/*"), {
2174
+ cwd: config.workspace,
2175
+ base: config.src.font
2176
+ });
2177
+ await fontPipeline.dest(config.dest.font);
2178
+ const targetFontsDir = path25.join(destBase, "fonts");
2179
+ if (config.dest.font !== targetFontsDir) {
2180
+ const fontsPipeline = Pipeline.from(path25.join(config.src.font, "**/*"), {
2181
+ cwd: config.workspace,
2182
+ base: config.src.font
2183
+ });
2184
+ await fontsPipeline.dest(targetFontsDir);
2185
+ }
2186
+ const targetFontDir = path25.join(destBase, "font");
2187
+ if (config.dest.font !== targetFontDir) {
2188
+ const fontCompatPipeline = Pipeline.from(path25.join(config.src.font, "**/*"), {
2189
+ cwd: config.workspace,
2190
+ base: config.src.font
2191
+ });
2192
+ await fontCompatPipeline.dest(targetFontDir);
2193
+ }
2194
+ }
2195
+ if (config.src.media && existsSync3(config.src.media)) {
2196
+ const mediaPipeline = Pipeline.from(path25.join(config.src.media, "**/*"), {
2197
+ cwd: config.workspace,
2198
+ base: config.src.media
2199
+ });
2200
+ await mediaPipeline.dest(config.dest.media);
2201
+ }
2202
+ const publicDir = path25.join(config.workspace, "public");
2203
+ if (existsSync3(publicDir)) {
2204
+ const pubPipeline = Pipeline.from(path25.join(publicDir, "**/*"), {
2205
+ cwd: config.workspace,
2206
+ base: publicDir
2207
+ });
2208
+ await pubPipeline.dest(destBase);
2209
+ }
2210
+ }
2211
+ };
2212
+
2213
+ // src/hooks/index.ts
2214
+ var BalmHooks = class {
2215
+ dag;
2216
+ _config;
2217
+ recipeIndex = 0;
2218
+ constructor(dag, config) {
2219
+ this.dag = dag;
2220
+ this._config = config;
2221
+ }
2222
+ get config() {
2223
+ return this._config;
2224
+ }
2225
+ get env() {
2226
+ return this._config.env;
2227
+ }
2228
+ addRecipeTask(fn) {
2229
+ this.recipeIndex++;
2230
+ const taskName = `recipe:${this.recipeIndex}`;
2231
+ this.dag.registerTask(new FunctionTask(taskName, fn));
2232
+ }
2233
+ copy(input, output, options = {}) {
2234
+ this.addRecipeTask(async (config) => {
2235
+ const task = new CopyTask({ input, output, rename: options.rename });
2236
+ await task.run(config);
2237
+ });
2238
+ return this;
2239
+ }
2240
+ remove(target) {
2241
+ this.addRecipeTask(async (config) => {
2242
+ const task = new RemoveTask(target);
2243
+ await task.run(config);
2244
+ });
2245
+ return this;
2246
+ }
2247
+ replace(input, output, options) {
2248
+ this.addRecipeTask(async (config) => {
2249
+ const task = new ReplaceTask({ input, output, options });
2250
+ await task.run(config);
2251
+ });
2252
+ return this;
2253
+ }
2254
+ sass(input, output, options = {}) {
2255
+ this.addRecipeTask(async (config) => {
2256
+ const pipeline = Pipeline.from(input, { cwd: config.workspace });
2257
+ pipeline.pipe(transformSass(options));
2258
+ pipeline.pipe(transformPostcss({ minify: config.env.isProd }));
2259
+ await pipeline.dest(output);
2260
+ });
2261
+ return this;
2262
+ }
2263
+ less(input, output, options = {}) {
2264
+ this.addRecipeTask(async (config) => {
2265
+ const pipeline = Pipeline.from(input, { cwd: config.workspace });
2266
+ pipeline.pipe(transformLess(options));
2267
+ pipeline.pipe(transformPostcss({ minify: config.env.isProd }));
2268
+ await pipeline.dest(output);
2269
+ });
2270
+ return this;
2271
+ }
2272
+ css(input, output, options = {}) {
2273
+ this.addRecipeTask(async (config) => {
2274
+ const pipeline = Pipeline.from(input, { cwd: config.workspace });
2275
+ pipeline.pipe(transformPostcss({ minify: config.env.isProd, ...options }));
2276
+ await pipeline.dest(output);
2277
+ });
2278
+ return this;
2279
+ }
2280
+ webpack(entry, output, customOptions = {}) {
2281
+ this.addRecipeTask(async (config) => {
2282
+ const mergedOptions = {
2283
+ ...customOptions,
2284
+ ...entry ? { entry } : {},
2285
+ output: {
2286
+ ...output ? { path: output } : {},
2287
+ ...customOptions.output
2288
+ }
2289
+ };
2290
+ await runWebpack(config, mergedOptions);
2291
+ });
2292
+ return this;
2293
+ }
2294
+ rollup(input, output, customInputOptions = {}, customOutputOptions = {}) {
2295
+ this.addRecipeTask(async (config) => {
2296
+ let inputOptions = {};
2297
+ let outputOptions = {};
2298
+ if (typeof input === "object" && !Array.isArray(input) && input.input) {
2299
+ inputOptions = { ...input, ...customInputOptions };
2300
+ outputOptions = typeof output === "object" ? { ...output, ...customOutputOptions } : { ...output ? { dir: output } : {}, ...customOutputOptions };
2301
+ } else {
2302
+ inputOptions = { ...input ? { input } : {}, ...customInputOptions };
2303
+ outputOptions = typeof output === "object" ? { ...output, ...customOutputOptions } : { ...output ? { dir: output } : {}, ...customOutputOptions };
2304
+ }
2305
+ await runRollup(config, inputOptions, outputOptions);
2306
+ });
2307
+ return this;
2308
+ }
2309
+ esbuild(entryPoints, outdir, customOptions = {}) {
2310
+ this.addRecipeTask(async (config) => {
2311
+ await runEsbuild(config, {
2312
+ ...entryPoints ? { entryPoints } : {},
2313
+ ...outdir ? { outdir } : {},
2314
+ ...customOptions
2315
+ });
2316
+ });
2317
+ return this;
2318
+ }
2319
+ js(input, output, options = {}) {
2320
+ logger.warn("balm api", "`mix.js` is deprecated, please use `mix.webpack`/`mix.rollup`/`mix.esbuild` instead.");
2321
+ return this.webpack(input, output, options);
2322
+ }
2323
+ publish(input, output, renameOptions) {
2324
+ this.addRecipeTask(async (config) => {
2325
+ const task = new PublishTask({ input, output, renameOptions });
2326
+ await task.run(config);
2327
+ });
2328
+ return this;
2329
+ }
2330
+ generateSW(options = {}) {
2331
+ this.addRecipeTask(async (config) => {
2332
+ const task = new PwaTask({ mode: "generateSW", options });
2333
+ await task.run(config);
2334
+ });
2335
+ return this;
2336
+ }
2337
+ injectManifest(options = {}) {
2338
+ this.addRecipeTask(async (config) => {
2339
+ const task = new PwaTask({ mode: "injectManifest", options });
2340
+ await task.run(config);
2341
+ });
2342
+ return this;
2343
+ }
2344
+ ftp(localFiles, options = {}) {
2345
+ this.addRecipeTask(async (config) => {
2346
+ const task = new FtpTask({ localFiles, options });
2347
+ await task.run(config);
2348
+ });
2349
+ return this;
2350
+ }
2351
+ zip(input, output, filename) {
2352
+ this.addRecipeTask(async (config) => {
2353
+ const task = new ZipTask({ input, output, filename });
2354
+ await task.run(config);
2355
+ });
2356
+ return this;
2357
+ }
2358
+ serve() {
2359
+ this.addRecipeTask(async (config) => {
2360
+ const task = new ServerTask();
2361
+ await task.run(config);
2362
+ });
2363
+ return this;
2364
+ }
2365
+ };
2366
+
2367
+ // src/balm.ts
2368
+ var Balm = class {
2369
+ _config;
2370
+ dag = new TaskDAG();
2371
+ beforeTask;
2372
+ afterTask;
2373
+ Bundler = BundlerType;
2374
+ constructor() {
2375
+ this._config = resolveConfig();
2376
+ }
2377
+ get config() {
2378
+ return this._config;
2379
+ }
2380
+ set config(customConfig) {
2381
+ this._config = resolveConfig(customConfig);
2382
+ if (this._config.logs?.level !== void 0) {
2383
+ logger.level = this._config.logs.level;
2384
+ }
2385
+ }
2386
+ async go(recipe) {
2387
+ const endTimer = logger.time("Build");
2388
+ const taskSequence = [];
2389
+ this.dag.registerTask(new CleanTask());
2390
+ this.dag.registerTask(new StyleTask());
2391
+ this.dag.registerTask(new ScriptTask());
2392
+ this.dag.registerTask(new StaticTask());
2393
+ this.dag.registerTask(new HtmlTask());
2394
+ this.dag.registerTask(new CacheTask());
2395
+ this.dag.registerTask(new PwaTask());
2396
+ this.dag.registerTask(new ServerTask());
2397
+ if (typeof recipe === "function") {
2398
+ const hooks = new BalmHooks(this.dag, this._config);
2399
+ recipe(hooks);
2400
+ }
2401
+ try {
2402
+ if (this.beforeTask) {
2403
+ await this.beforeTask();
2404
+ }
2405
+ if (this._config.useDefaults) {
2406
+ await this.dag.runSeries(
2407
+ ["clean", "style", "script", "static", "html"],
2408
+ this._config
2409
+ );
2410
+ }
2411
+ for (let i = 1; ; i++) {
2412
+ const recipeName = `recipe:${i}`;
2413
+ if (!this.dag.hasTask(recipeName)) break;
2414
+ await this.dag.executeTask(recipeName, this._config);
2415
+ }
2416
+ if (this._config.useDefaults) {
2417
+ if (this._config.env.isProd) {
2418
+ if (this._config.assets.cache) {
2419
+ await this.dag.executeTask("cache", this._config);
2420
+ }
2421
+ if (this._config.pwa.enabled) {
2422
+ await this.dag.executeTask("pwa", this._config);
2423
+ }
2424
+ } else {
2425
+ await this.dag.executeTask("serve", this._config);
2426
+ }
2427
+ }
2428
+ if (this.afterTask) {
2429
+ await this.afterTask();
2430
+ }
2431
+ endTimer();
2432
+ } catch (err) {
2433
+ logger.error("build", `Build failed: ${err?.message || err}`);
2434
+ throw err;
2435
+ }
2436
+ }
2437
+ reset() {
2438
+ this.dag.clear();
2439
+ this.beforeTask = void 0;
2440
+ this.afterTask = void 0;
2441
+ this._config = resolveConfig();
2442
+ }
2443
+ };
2444
+ var balm = new Balm();
2445
+
2446
+ // src/pipeline/transforms/terser.ts
2447
+ import { minify as minify2 } from "terser";
2448
+ function transformTerser(options = {}) {
2449
+ return async (file2) => {
2450
+ if (!/\.js$/i.test(file2.extname)) return file2;
2451
+ try {
2452
+ const result = await minify2(file2.toString(), options);
2453
+ if (result.code) {
2454
+ file2.contents = Buffer.from(result.code);
2455
+ }
2456
+ return file2;
2457
+ } catch (err) {
2458
+ logger.error("terser", `Error minifying ${file2.path}: ${err.message}`);
2459
+ throw err;
2460
+ }
2461
+ };
2462
+ }
2463
+
2464
+ // src/index.ts
2465
+ var index_default = balm;
2466
+ export {
2467
+ ASSET,
2468
+ ASSETS_TYPES,
2469
+ ASSET_EXT,
2470
+ AssetRevisioner,
2471
+ Balm,
2472
+ BalmHooks,
2473
+ BaseTask,
2474
+ BundlerType,
2475
+ CHUNK,
2476
+ CacheTask,
2477
+ CleanTask,
2478
+ CopyTask,
2479
+ FileSystem,
2480
+ FtpTask,
2481
+ FunctionTask,
2482
+ HtmlTask,
2483
+ LogLevel,
2484
+ PUBLIC_URL,
2485
+ Pipeline,
2486
+ PublishTask,
2487
+ PwaTask,
2488
+ RemoveTask,
2489
+ ReplaceTask,
2490
+ ScriptTask,
2491
+ ServerTask,
2492
+ StaticTask,
2493
+ StyleTask,
2494
+ TaskDAG,
2495
+ VirtualFile,
2496
+ ZipTask,
2497
+ balm,
2498
+ createDefaultConfig,
2499
+ deepMerge,
2500
+ index_default as default,
2501
+ file,
2502
+ getWorkspaces,
2503
+ globalResolve,
2504
+ globalWorkspace,
2505
+ isArray,
2506
+ isBoolean,
2507
+ isFunction,
2508
+ isObject,
2509
+ isString,
2510
+ localResolve,
2511
+ localWorkspace,
2512
+ logger,
2513
+ resolveConfig,
2514
+ runEsbuild,
2515
+ runRollup,
2516
+ runWebpack,
2517
+ setWorkspaces,
2518
+ toNamespace,
2519
+ transformHtmlmin,
2520
+ transformLess,
2521
+ transformPostcss,
2522
+ transformRename,
2523
+ transformReplace,
2524
+ transformSass,
2525
+ transformTerser
2526
+ };
2527
+ //# sourceMappingURL=index.js.map