balm-core 5.2.0 → 6.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (299) hide show
  1. package/dist/index.cjs +2191 -0
  2. package/dist/index.cjs.map +1 -0
  3. package/dist/index.d.cts +559 -0
  4. package/dist/index.d.ts +559 -0
  5. package/dist/index.js +2098 -0
  6. package/dist/index.js.map +1 -0
  7. package/package.json +48 -123
  8. package/src/balm.ts +103 -0
  9. package/src/bundler/esbuild.ts +47 -0
  10. package/src/bundler/index.ts +3 -0
  11. package/src/bundler/rollup.ts +39 -0
  12. package/src/bundler/webpack.ts +82 -0
  13. package/src/config/constants.ts +12 -0
  14. package/src/config/defaults.ts +209 -0
  15. package/src/config/index.ts +112 -0
  16. package/src/hooks/index.ts +192 -0
  17. package/src/index.ts +16 -0
  18. package/src/pipeline/file.ts +69 -0
  19. package/src/pipeline/index.ts +10 -0
  20. package/src/pipeline/pipeline.ts +132 -0
  21. package/src/pipeline/transforms/cache.ts +96 -0
  22. package/src/pipeline/transforms/htmlmin.ts +18 -0
  23. package/src/pipeline/transforms/less.ts +75 -0
  24. package/src/pipeline/transforms/postcss.ts +43 -0
  25. package/src/pipeline/transforms/rename.ts +37 -0
  26. package/src/pipeline/transforms/replace.ts +30 -0
  27. package/src/pipeline/transforms/sass.ts +106 -0
  28. package/src/pipeline/transforms/terser.ts +20 -0
  29. package/src/runner/dag.ts +77 -0
  30. package/src/runner/task.ts +38 -0
  31. package/src/shims.d.ts +5 -0
  32. package/src/tasks/cache.ts +58 -0
  33. package/src/tasks/clean.ts +21 -0
  34. package/src/tasks/copy.ts +36 -0
  35. package/src/tasks/ftp.ts +90 -0
  36. package/src/tasks/html.ts +78 -0
  37. package/src/tasks/index.ts +13 -0
  38. package/src/tasks/publish.ts +71 -0
  39. package/src/tasks/pwa.ts +49 -0
  40. package/src/tasks/remove.ts +24 -0
  41. package/src/tasks/replace.ts +34 -0
  42. package/src/tasks/script.ts +28 -0
  43. package/src/tasks/server.ts +89 -0
  44. package/src/tasks/style.ts +57 -0
  45. package/src/tasks/zip.ts +65 -0
  46. package/src/types/index.ts +200 -0
  47. package/src/utilities/file.ts +120 -0
  48. package/src/utilities/logger.ts +63 -0
  49. package/src/utilities/utils.ts +46 -0
  50. package/src/utilities/workspace.ts +38 -0
  51. package/src/watcher/index.ts +100 -0
  52. package/tsconfig.json +8 -0
  53. package/tsup.config.ts +12 -0
  54. package/README.md +0 -196
  55. package/index.d.ts +0 -360
  56. package/lib/balm.js +0 -49
  57. package/lib/bootstrap/check_config.js +0 -43
  58. package/lib/bootstrap/index.js +0 -87
  59. package/lib/bundler/esbuild/build.js +0 -26
  60. package/lib/bundler/esbuild/index.js +0 -7
  61. package/lib/bundler/esbuild/options.js +0 -7
  62. package/lib/bundler/esbuild/transform.js +0 -36
  63. package/lib/bundler/rollup/build.js +0 -52
  64. package/lib/bundler/rollup/entry.js +0 -28
  65. package/lib/bundler/rollup/index.js +0 -20
  66. package/lib/bundler/rollup/output.js +0 -20
  67. package/lib/bundler/rollup/plugins.js +0 -31
  68. package/lib/bundler/webpack/config/common.js +0 -135
  69. package/lib/bundler/webpack/config/dev.js +0 -12
  70. package/lib/bundler/webpack/config/env.js +0 -67
  71. package/lib/bundler/webpack/config/index.js +0 -6
  72. package/lib/bundler/webpack/config/prod.js +0 -39
  73. package/lib/bundler/webpack/config/regex.js +0 -10
  74. package/lib/bundler/webpack/entry.js +0 -65
  75. package/lib/bundler/webpack/index.js +0 -32
  76. package/lib/bundler/webpack/loaders.js +0 -50
  77. package/lib/bundler/webpack/output.js +0 -41
  78. package/lib/bundler/webpack/rules/asset.js +0 -33
  79. package/lib/bundler/webpack/rules/babel.js +0 -19
  80. package/lib/bundler/webpack/rules/css.js +0 -109
  81. package/lib/bundler/webpack/rules/html.js +0 -15
  82. package/lib/bundler/webpack/rules/index.js +0 -10
  83. package/lib/config/assets.js +0 -30
  84. package/lib/config/constants.js +0 -14
  85. package/lib/config/env.js +0 -35
  86. package/lib/config/extras.js +0 -6
  87. package/lib/config/ftp.js +0 -6
  88. package/lib/config/globals.js +0 -43
  89. package/lib/config/html.js +0 -22
  90. package/lib/config/images.js +0 -4
  91. package/lib/config/index.js +0 -63
  92. package/lib/config/logs.js +0 -11
  93. package/lib/config/paths.js +0 -46
  94. package/lib/config/pwa.js +0 -24
  95. package/lib/config/roots.js +0 -9
  96. package/lib/config/scripts.js +0 -162
  97. package/lib/config/server.js +0 -84
  98. package/lib/config/spinner.js +0 -9
  99. package/lib/config/styles.js +0 -67
  100. package/lib/hello.js +0 -19
  101. package/lib/hooks/index.js +0 -94
  102. package/lib/hooks/maker.js +0 -59
  103. package/lib/index.js +0 -5
  104. package/lib/middlewares/history.js +0 -6
  105. package/lib/middlewares/index.js +0 -11
  106. package/lib/middlewares/proxy.js +0 -28
  107. package/lib/middlewares/webpack.js +0 -26
  108. package/lib/plugins/htmlmin.js +0 -39
  109. package/lib/plugins/imagemin.js +0 -95
  110. package/lib/plugins/index.js +0 -27
  111. package/lib/plugins/jsmin.js +0 -37
  112. package/lib/plugins/less.js +0 -97
  113. package/lib/plugins/plumber.js +0 -87
  114. package/lib/plugins/postcss-plugins.js +0 -13
  115. package/lib/plugins/postcss.js +0 -117
  116. package/lib/plugins/rename.js +0 -51
  117. package/lib/plugins/replace.js +0 -91
  118. package/lib/plugins/sass-legacy.js +0 -131
  119. package/lib/plugins/sass.js +0 -134
  120. package/lib/plugins/sftp.js +0 -288
  121. package/lib/tasks/default.js +0 -46
  122. package/lib/tasks/foundation/balm.js +0 -72
  123. package/lib/tasks/foundation/balm_style.js +0 -65
  124. package/lib/tasks/foundation/index.js +0 -10
  125. package/lib/tasks/index.js +0 -48
  126. package/lib/tasks/private/build.js +0 -18
  127. package/lib/tasks/private/cache.js +0 -31
  128. package/lib/tasks/private/clean.js +0 -69
  129. package/lib/tasks/private/end.js +0 -24
  130. package/lib/tasks/private/extra.js +0 -35
  131. package/lib/tasks/private/font.js +0 -15
  132. package/lib/tasks/private/image.js +0 -37
  133. package/lib/tasks/private/index.js +0 -34
  134. package/lib/tasks/private/lint.js +0 -20
  135. package/lib/tasks/private/media.js +0 -15
  136. package/lib/tasks/private/modernizr.js +0 -59
  137. package/lib/tasks/private/pwa-cache.js +0 -32
  138. package/lib/tasks/private/script.js +0 -9
  139. package/lib/tasks/private/sprite.js +0 -105
  140. package/lib/tasks/private/start.js +0 -16
  141. package/lib/tasks/private/style.js +0 -9
  142. package/lib/tasks/private/workbox-sw.js +0 -15
  143. package/lib/tasks/public/copy.js +0 -16
  144. package/lib/tasks/public/esbuild.js +0 -18
  145. package/lib/tasks/public/ftp.js +0 -39
  146. package/lib/tasks/public/html.js +0 -65
  147. package/lib/tasks/public/index.js +0 -38
  148. package/lib/tasks/public/jsmin.js +0 -23
  149. package/lib/tasks/public/less.js +0 -21
  150. package/lib/tasks/public/postcss.js +0 -16
  151. package/lib/tasks/public/publish.js +0 -51
  152. package/lib/tasks/public/pwa.js +0 -84
  153. package/lib/tasks/public/remove.js +0 -46
  154. package/lib/tasks/public/replace.js +0 -26
  155. package/lib/tasks/public/rollup.js +0 -17
  156. package/lib/tasks/public/sass.js +0 -27
  157. package/lib/tasks/public/server.js +0 -130
  158. package/lib/tasks/public/url.js +0 -35
  159. package/lib/tasks/public/version.js +0 -23
  160. package/lib/tasks/public/webpack.js +0 -39
  161. package/lib/tasks/public/zip.js +0 -19
  162. package/lib/utilities/address.js +0 -55
  163. package/lib/utilities/cache-core.js +0 -66
  164. package/lib/utilities/color.js +0 -27
  165. package/lib/utilities/compiling.js +0 -24
  166. package/lib/utilities/detect-port.js +0 -100
  167. package/lib/utilities/escape-string-regexp.js +0 -10
  168. package/lib/utilities/file.js +0 -70
  169. package/lib/utilities/index.js +0 -8
  170. package/lib/utilities/loading.js +0 -37
  171. package/lib/utilities/logger.js +0 -99
  172. package/lib/utilities/merge-stream.js +0 -38
  173. package/lib/utilities/namespace.js +0 -11
  174. package/lib/utilities/public-url.js +0 -37
  175. package/lib/utilities/replacestream.js +0 -106
  176. package/lib/utilities/typeof.js +0 -5
  177. package/lib/utilities/utils.js +0 -43
  178. package/tslib/balm.js +0 -44
  179. package/tslib/bootstrap/check_config.js +0 -36
  180. package/tslib/bootstrap/index.js +0 -82
  181. package/tslib/bundler/esbuild/build.js +0 -30
  182. package/tslib/bundler/esbuild/index.js +0 -11
  183. package/tslib/bundler/esbuild/options.js +0 -7
  184. package/tslib/bundler/esbuild/transform.js +0 -38
  185. package/tslib/bundler/rollup/build.js +0 -17
  186. package/tslib/bundler/rollup/entry.js +0 -24
  187. package/tslib/bundler/rollup/index.js +0 -21
  188. package/tslib/bundler/rollup/output.js +0 -23
  189. package/tslib/bundler/rollup/plugins.js +0 -27
  190. package/tslib/bundler/webpack/config/common.js +0 -130
  191. package/tslib/bundler/webpack/config/dev.js +0 -14
  192. package/tslib/bundler/webpack/config/env.js +0 -44
  193. package/tslib/bundler/webpack/config/index.js +0 -8
  194. package/tslib/bundler/webpack/config/prod.js +0 -43
  195. package/tslib/bundler/webpack/config/regex.js +0 -10
  196. package/tslib/bundler/webpack/entry.js +0 -68
  197. package/tslib/bundler/webpack/index.js +0 -30
  198. package/tslib/bundler/webpack/loaders.js +0 -51
  199. package/tslib/bundler/webpack/output.js +0 -41
  200. package/tslib/bundler/webpack/rules/asset.js +0 -33
  201. package/tslib/bundler/webpack/rules/babel.js +0 -16
  202. package/tslib/bundler/webpack/rules/css.js +0 -95
  203. package/tslib/bundler/webpack/rules/html.js +0 -15
  204. package/tslib/bundler/webpack/rules/index.js +0 -10
  205. package/tslib/config/assets.js +0 -24
  206. package/tslib/config/constants.js +0 -14
  207. package/tslib/config/env.js +0 -43
  208. package/tslib/config/extras.js +0 -6
  209. package/tslib/config/ftp.js +0 -6
  210. package/tslib/config/globals.js +0 -47
  211. package/tslib/config/html.js +0 -17
  212. package/tslib/config/images.js +0 -4
  213. package/tslib/config/index.js +0 -56
  214. package/tslib/config/logs.js +0 -6
  215. package/tslib/config/paths.js +0 -30
  216. package/tslib/config/pwa.js +0 -18
  217. package/tslib/config/roots.js +0 -8
  218. package/tslib/config/scripts.js +0 -93
  219. package/tslib/config/server.js +0 -33
  220. package/tslib/config/spinner.js +0 -10
  221. package/tslib/config/styles.js +0 -37
  222. package/tslib/hello.js +0 -19
  223. package/tslib/hooks/index.js +0 -75
  224. package/tslib/hooks/maker.js +0 -66
  225. package/tslib/index.js +0 -5
  226. package/tslib/middlewares/history.js +0 -12
  227. package/tslib/middlewares/index.js +0 -18
  228. package/tslib/middlewares/proxy.js +0 -27
  229. package/tslib/middlewares/webpack.js +0 -29
  230. package/tslib/plugins/htmlmin.js +0 -38
  231. package/tslib/plugins/imagemin.js +0 -97
  232. package/tslib/plugins/index.js +0 -27
  233. package/tslib/plugins/jsmin.js +0 -39
  234. package/tslib/plugins/less.js +0 -90
  235. package/tslib/plugins/plumber.js +0 -81
  236. package/tslib/plugins/postcss-plugins.js +0 -24
  237. package/tslib/plugins/postcss.js +0 -111
  238. package/tslib/plugins/rename.js +0 -51
  239. package/tslib/plugins/replace.js +0 -72
  240. package/tslib/plugins/sass-legacy.js +0 -95
  241. package/tslib/plugins/sass.js +0 -109
  242. package/tslib/plugins/sftp.js +0 -258
  243. package/tslib/tasks/default.js +0 -59
  244. package/tslib/tasks/foundation/balm.js +0 -75
  245. package/tslib/tasks/foundation/balm_style.js +0 -68
  246. package/tslib/tasks/foundation/index.js +0 -10
  247. package/tslib/tasks/index.js +0 -43
  248. package/tslib/tasks/private/build.js +0 -15
  249. package/tslib/tasks/private/cache.js +0 -48
  250. package/tslib/tasks/private/clean.js +0 -82
  251. package/tslib/tasks/private/end.js +0 -21
  252. package/tslib/tasks/private/extra.js +0 -34
  253. package/tslib/tasks/private/font.js +0 -12
  254. package/tslib/tasks/private/image.js +0 -42
  255. package/tslib/tasks/private/index.js +0 -34
  256. package/tslib/tasks/private/lint.js +0 -17
  257. package/tslib/tasks/private/media.js +0 -12
  258. package/tslib/tasks/private/modernizr.js +0 -54
  259. package/tslib/tasks/private/pwa-cache.js +0 -32
  260. package/tslib/tasks/private/script.js +0 -9
  261. package/tslib/tasks/private/sprite.js +0 -93
  262. package/tslib/tasks/private/start.js +0 -13
  263. package/tslib/tasks/private/style.js +0 -12
  264. package/tslib/tasks/private/workbox-sw.js +0 -15
  265. package/tslib/tasks/public/copy.js +0 -18
  266. package/tslib/tasks/public/esbuild.js +0 -18
  267. package/tslib/tasks/public/ftp.js +0 -39
  268. package/tslib/tasks/public/html.js +0 -75
  269. package/tslib/tasks/public/index.js +0 -38
  270. package/tslib/tasks/public/jsmin.js +0 -24
  271. package/tslib/tasks/public/less.js +0 -21
  272. package/tslib/tasks/public/postcss.js +0 -16
  273. package/tslib/tasks/public/publish.js +0 -50
  274. package/tslib/tasks/public/pwa.js +0 -87
  275. package/tslib/tasks/public/remove.js +0 -44
  276. package/tslib/tasks/public/replace.js +0 -27
  277. package/tslib/tasks/public/rollup.js +0 -17
  278. package/tslib/tasks/public/sass.js +0 -27
  279. package/tslib/tasks/public/server.js +0 -129
  280. package/tslib/tasks/public/url.js +0 -35
  281. package/tslib/tasks/public/version.js +0 -27
  282. package/tslib/tasks/public/webpack.js +0 -41
  283. package/tslib/tasks/public/zip.js +0 -19
  284. package/tslib/utilities/address.js +0 -48
  285. package/tslib/utilities/cache-core.js +0 -57
  286. package/tslib/utilities/color.js +0 -33
  287. package/tslib/utilities/compiling.js +0 -16
  288. package/tslib/utilities/detect-port.js +0 -91
  289. package/tslib/utilities/escape-string-regexp.js +0 -7
  290. package/tslib/utilities/file.js +0 -78
  291. package/tslib/utilities/index.js +0 -8
  292. package/tslib/utilities/loading.js +0 -25
  293. package/tslib/utilities/logger.js +0 -92
  294. package/tslib/utilities/merge-stream.js +0 -34
  295. package/tslib/utilities/namespace.js +0 -12
  296. package/tslib/utilities/public-url.js +0 -31
  297. package/tslib/utilities/replacestream.js +0 -112
  298. package/tslib/utilities/typeof.js +0 -8
  299. package/tslib/utilities/utils.js +0 -38
@@ -0,0 +1,89 @@
1
+ import http from 'node:http';
2
+ import connect from 'connect';
3
+ import serveStatic from 'serve-static';
4
+ import { WebSocketServer, WebSocket } from 'ws';
5
+ import { createProxyMiddleware } from 'http-proxy-middleware';
6
+ import { BaseTask } from '../runner/task.js';
7
+ import { BalmConfig } from '../types/index.js';
8
+ import { FileWatcher } from '../watcher/index.js';
9
+ import { logger } from '../utilities/logger.js';
10
+ import pc from 'picocolors';
11
+
12
+ export class ServerTask extends BaseTask {
13
+ private watcher: FileWatcher | null = null;
14
+ private wsServer: WebSocketServer | null = null;
15
+ private clients = new Set<WebSocket>();
16
+
17
+ constructor() {
18
+ super({ name: 'serve', description: 'Start development server with live reload' });
19
+ }
20
+
21
+ broadcast(data: { type: 'reload' | 'reload-css'; file?: string }): void {
22
+ const payload = JSON.stringify(data);
23
+ for (const client of this.clients) {
24
+ if (client.readyState === WebSocket.OPEN) {
25
+ client.send(payload);
26
+ }
27
+ }
28
+ }
29
+
30
+ async run(config: BalmConfig): Promise<void> {
31
+ if (config.env.isProd) {
32
+ logger.warn('server', 'Development server is disabled in production mode');
33
+ return;
34
+ }
35
+
36
+ const app = connect();
37
+ const port = config.server.port || 3000;
38
+ const host = config.server.host || 'localhost';
39
+
40
+ // Static assets
41
+ app.use(serveStatic(config.dest.base));
42
+ app.use(serveStatic(config.src.base));
43
+
44
+ // Proxy middleware
45
+ if (config.server.proxy && typeof config.server.proxy === 'object') {
46
+ const proxyEntries = Array.isArray(config.server.proxy)
47
+ ? config.server.proxy
48
+ : [config.server.proxy];
49
+
50
+ for (const entry of proxyEntries) {
51
+ if (entry.context && entry.options) {
52
+ app.use(createProxyMiddleware(entry.options));
53
+ }
54
+ }
55
+ }
56
+
57
+ const server = http.createServer(app);
58
+
59
+ // WebSocket Live Reload Server
60
+ this.wsServer = new WebSocketServer({ server, path: '/__balm_ws' });
61
+ this.wsServer.on('connection', (socket) => {
62
+ this.clients.add(socket);
63
+ socket.on('close', () => this.clients.delete(socket));
64
+ });
65
+
66
+ // Start File Watcher
67
+ this.watcher = new FileWatcher(config, (data) => {
68
+ this.broadcast(data);
69
+ });
70
+ this.watcher.start();
71
+
72
+ return new Promise((resolve) => {
73
+ server.listen(port, host, () => {
74
+ console.log('');
75
+ logger.success('server', pc.bold('BalmJS Dev Server is ready!'));
76
+ console.log(` ${pc.green('➜')} ${pc.bold('Local:')} ${pc.cyan(`http://${host}:${port}/`)}`);
77
+ console.log(` ${pc.green('➜')} ${pc.bold('Network:')} ${pc.cyan(`http://127.0.0.1:${port}/`)}`);
78
+ console.log('');
79
+
80
+ if (config.env.isTest) {
81
+ this.watcher?.stop();
82
+ this.wsServer?.close();
83
+ server.close();
84
+ resolve();
85
+ }
86
+ });
87
+ });
88
+ }
89
+ }
@@ -0,0 +1,57 @@
1
+ import path from 'node:path';
2
+ import { BaseTask } from '../runner/task.js';
3
+ import { BalmConfig } from '../types/index.js';
4
+ import { Pipeline } from '../pipeline/pipeline.js';
5
+ import { transformSass } from '../pipeline/transforms/sass.js';
6
+ import { transformLess } from '../pipeline/transforms/less.js';
7
+ import { transformPostcss } from '../pipeline/transforms/postcss.js';
8
+
9
+ export class StyleTask extends BaseTask {
10
+ constructor() {
11
+ super({ name: 'style', description: 'Compile stylesheets' });
12
+ }
13
+
14
+ async run(config: BalmConfig): Promise<void> {
15
+ const ext = config.styles.extname;
16
+ const srcPattern = path.join(config.src.css, `**/*.${ext}`);
17
+ const isProd = config.env.isProd;
18
+
19
+ const pipeline = Pipeline.from([srcPattern, `!${path.join(config.src.css, '**/_*.scss')}`, `!${path.join(config.src.css, '**/_*.less')}`], {
20
+ cwd: config.workspace,
21
+ base: config.src.css
22
+ });
23
+
24
+ const styleAlias = {
25
+ ...config.alias,
26
+ ...config.styles.alias
27
+ };
28
+
29
+ if (ext === 'scss' || ext === 'sass') {
30
+ pipeline.pipe(
31
+ transformSass({
32
+ includePaths: [config.src.css, ...config.styles.atImportPaths],
33
+ alias: styleAlias,
34
+ ...config.styles.sassOptions
35
+ })
36
+ );
37
+ } else if (ext === 'less') {
38
+ pipeline.pipe(
39
+ transformLess({
40
+ paths: [config.src.css, ...config.styles.atImportPaths],
41
+ alias: styleAlias,
42
+ ...config.styles.lessOptions
43
+ })
44
+ );
45
+ }
46
+
47
+ pipeline.pipe(
48
+ transformPostcss({
49
+ plugins: config.styles.postcssPlugins,
50
+ minify: isProd || config.styles.minify,
51
+ ...config.styles.postcssLoaderOptions
52
+ })
53
+ );
54
+
55
+ await pipeline.dest(config.dest.css);
56
+ }
57
+ }
@@ -0,0 +1,65 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import archiver from 'archiver';
4
+ import { BaseTask } from '../runner/task.js';
5
+ import { BalmConfig } from '../types/index.js';
6
+ import { file as fsUtil } from '../utilities/file.js';
7
+ import { logger } from '../utilities/logger.js';
8
+
9
+ export interface ZipTaskOptions {
10
+ input?: string | string[];
11
+ output?: string;
12
+ filename?: string;
13
+ }
14
+
15
+ export class ZipTask extends BaseTask {
16
+ private options: ZipTaskOptions;
17
+
18
+ constructor(options: ZipTaskOptions = {}) {
19
+ super({ name: 'zip', description: 'Compress files into archive' });
20
+ this.options = options;
21
+ }
22
+
23
+ async run(config: BalmConfig): Promise<void> {
24
+ const input = this.options.input || config.dest.base;
25
+ const outputDir = this.options.output
26
+ ? (path.isAbsolute(this.options.output) ? this.options.output : path.join(config.workspace, this.options.output))
27
+ : config.workspace;
28
+ const filename = this.options.filename || 'archive.zip';
29
+ const zipPath = path.join(outputDir, filename);
30
+
31
+ await fsUtil.ensureDir(outputDir);
32
+
33
+ return new Promise((resolve, reject) => {
34
+ const outputStream = fs.createWriteStream(zipPath);
35
+ const archive = archiver('zip', { zlib: { level: 9 } });
36
+
37
+ outputStream.on('close', () => {
38
+ logger.success('zip', `Created ${zipPath} (${archive.pointer()} total bytes)`);
39
+ resolve();
40
+ });
41
+
42
+ archive.on('error', (err) => {
43
+ logger.error('zip', err.message);
44
+ reject(err);
45
+ });
46
+
47
+ archive.pipe(outputStream);
48
+
49
+ const inputs = Array.isArray(input) ? input : [input];
50
+ for (const item of inputs) {
51
+ const fullPath = path.isAbsolute(item) ? item : path.join(config.workspace, item);
52
+ if (fs.existsSync(fullPath)) {
53
+ const stat = fs.statSync(fullPath);
54
+ if (stat.isDirectory()) {
55
+ archive.directory(fullPath, false);
56
+ } else {
57
+ archive.file(fullPath, { name: path.basename(fullPath) });
58
+ }
59
+ }
60
+ }
61
+
62
+ archive.finalize();
63
+ });
64
+ }
65
+ }
@@ -0,0 +1,200 @@
1
+ export type LooseObject = Record<string, any>;
2
+ export type DeepPartial<T> = {
3
+ [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
4
+ };
5
+
6
+ export type BalmEntry = string | string[] | Record<string, string | string[]>;
7
+
8
+ export interface BalmRoots {
9
+ source: string;
10
+ tmp: string;
11
+ target: string;
12
+ }
13
+
14
+ export interface BalmPathMap {
15
+ base: string;
16
+ html: string;
17
+ css: string;
18
+ js: string;
19
+ img: string;
20
+ font: string;
21
+ media: string;
22
+ }
23
+
24
+ export interface BalmPaths {
25
+ source: BalmPathMap;
26
+ tmp: Omit<BalmPathMap, 'html'>;
27
+ target: Omit<BalmPathMap, 'html'>;
28
+ }
29
+
30
+ export interface BalmStyles {
31
+ extname: string;
32
+ minify: boolean;
33
+ atImportPaths: string[];
34
+ alias: Record<string, string>;
35
+ options: Record<string, any>;
36
+ sassOptions: Record<string, any>;
37
+ lessOptions: Record<string, any>;
38
+ postcssLoaderOptions: Record<string, any>;
39
+ postcssPlugins: any[];
40
+ sprites: string[];
41
+ spriteOptions: Record<string, any>;
42
+ }
43
+
44
+ export enum BundlerType {
45
+ webpack = 'webpack',
46
+ rollup = 'rollup',
47
+ esbuild = 'esbuild'
48
+ }
49
+
50
+ export interface BalmScripts {
51
+ bundler: BundlerType | 'webpack' | 'rollup' | 'esbuild';
52
+ minify: boolean;
53
+ minifyOptions: Record<string, any>;
54
+ entry: BalmEntry;
55
+ target: string | string[];
56
+ library: string | Record<string, any>;
57
+ libraryTarget: string;
58
+ externals: string | string[] | Record<string, any> | RegExp | Function;
59
+ injectHtml: boolean;
60
+ htmlName: string;
61
+ alias: Record<string, string>;
62
+ plugins: any[];
63
+ loaders: any[];
64
+ defaultLoaders: {
65
+ html?: boolean;
66
+ css?: boolean;
67
+ js?: boolean;
68
+ url?: boolean;
69
+ };
70
+ options: Record<string, any>;
71
+ esbuildOptions: Record<string, any>;
72
+ rollupOptions: Record<string, any>;
73
+ webpackOptions: Record<string, any>;
74
+ useCache: boolean;
75
+ }
76
+
77
+ export interface BalmHtml {
78
+ options: Record<string, any>;
79
+ }
80
+
81
+ export interface BalmAssets {
82
+ root: string;
83
+ mainDir: string;
84
+ subDir: string;
85
+ buildDir: string;
86
+ virtualDir: string;
87
+ cache: boolean;
88
+ options: {
89
+ fileNameManifest?: string;
90
+ dontRenameFile?: (string | RegExp)[];
91
+ dontUpdateReference?: (string | RegExp)[];
92
+ hashLength?: number;
93
+ [key: string]: any;
94
+ };
95
+ includes: string[];
96
+ excludes: string[];
97
+ static?: string;
98
+ [key: string]: any;
99
+ }
100
+
101
+ export interface BalmServer {
102
+ host: string | null;
103
+ port: number;
104
+ open: boolean | string;
105
+ proxy: boolean | Record<string, any> | Array<Record<string, any>>;
106
+ proxyOptions: boolean | Record<string, any> | Array<Record<string, any>>;
107
+ historyOptions: boolean | Record<string, any>;
108
+ useHMR: boolean;
109
+ options: Record<string, any>;
110
+ }
111
+
112
+ export interface BalmPwa {
113
+ enabled: boolean;
114
+ mode: 'generateSW' | 'injectManifest';
115
+ version: string;
116
+ manifest: string;
117
+ swSrcFilename: string;
118
+ swDestFilename: string;
119
+ options: Record<string, any>;
120
+ }
121
+
122
+ export interface BalmFtp {
123
+ options: Record<string, any>;
124
+ watchFiles: string[];
125
+ }
126
+
127
+ export interface BalmImages {
128
+ plugins: any[];
129
+ }
130
+
131
+ export interface BalmEnv {
132
+ isProd: boolean;
133
+ isDev: boolean;
134
+ isTest: boolean;
135
+ inFrontend: boolean;
136
+ inSSR: boolean;
137
+ inDesktopApp: boolean;
138
+ isMP: boolean;
139
+ }
140
+
141
+ export interface BalmWorkspaces {
142
+ local: string;
143
+ global: string;
144
+ }
145
+
146
+ export type BalmWorkspaceConfig = string | { local?: string; global?: string };
147
+
148
+ export interface BalmConfig {
149
+ workspace: string;
150
+ workspaces: BalmWorkspaces;
151
+ alias: Record<string, string>;
152
+ env: BalmEnv;
153
+ inFrontend: boolean;
154
+ useDefaults: boolean;
155
+ roots: BalmRoots;
156
+ paths: BalmPaths;
157
+ styles: BalmStyles;
158
+ scripts: BalmScripts;
159
+ html: BalmHtml;
160
+ assets: BalmAssets;
161
+ server: BalmServer;
162
+ pwa: BalmPwa;
163
+ ftp: BalmFtp;
164
+ images: BalmImages;
165
+ logs: {
166
+ level: number;
167
+ };
168
+ src: BalmPathMap;
169
+ dest: BalmPathMap & { static: string };
170
+ [key: string]: any;
171
+ }
172
+
173
+ export type BalmCustomConfig = Omit<DeepPartial<BalmConfig>, 'workspace'> & {
174
+ workspace?: BalmWorkspaceConfig;
175
+ };
176
+
177
+ export interface RenameOptions {
178
+ dirname?: string;
179
+ prefix?: string;
180
+ basename?: string;
181
+ suffix?: string;
182
+ extname?: string;
183
+ }
184
+
185
+ export interface ReplaceOptions {
186
+ substr: string | RegExp;
187
+ replacement: string | ((substring: string, ...args: any[]) => string);
188
+ }
189
+
190
+ export interface TemplateOption {
191
+ input: string;
192
+ output: string;
193
+ renameOptions?: string | Function | RenameOptions;
194
+ }
195
+
196
+ export interface TaskContext {
197
+ config: BalmConfig;
198
+ }
199
+
200
+ export type TaskFn = (context: TaskContext) => Promise<any> | any;
@@ -0,0 +1,120 @@
1
+ import fs from 'node:fs/promises';
2
+ import { existsSync } from 'node:fs';
3
+ import path from 'node:path';
4
+ import crypto from 'node:crypto';
5
+ import fg from 'fast-glob';
6
+
7
+ export class FileSystem {
8
+ workspace: string = process.cwd();
9
+
10
+ resolveApp(...paths: string[]): string {
11
+ return path.resolve(this.workspace, ...paths);
12
+ }
13
+
14
+ absPath(filePath: string): string {
15
+ return path.isAbsolute(filePath) ? filePath : this.resolveApp(filePath);
16
+ }
17
+
18
+ absPaths(filePaths: string | string[]): string | string[] {
19
+ if (Array.isArray(filePaths)) {
20
+ return filePaths.map((p) => this.absPath(p));
21
+ }
22
+ return this.absPath(filePaths);
23
+ }
24
+
25
+ async exists(targetPath: string): Promise<boolean> {
26
+ try {
27
+ await fs.access(this.absPath(targetPath));
28
+ return true;
29
+ } catch {
30
+ return false;
31
+ }
32
+ }
33
+
34
+ existsSync(targetPath: string): boolean {
35
+ return existsSync(this.absPath(targetPath));
36
+ }
37
+
38
+ async ensureDir(dirPath: string): Promise<void> {
39
+ await fs.mkdir(this.absPath(dirPath), { recursive: true });
40
+ }
41
+
42
+ async readFile(filePath: string, encoding: BufferEncoding = 'utf8'): Promise<string> {
43
+ return fs.readFile(this.absPath(filePath), { encoding });
44
+ }
45
+
46
+ async readBuffer(filePath: string): Promise<Buffer> {
47
+ return fs.readFile(this.absPath(filePath));
48
+ }
49
+
50
+ async writeFile(filePath: string, data: string | Buffer): Promise<void> {
51
+ const fullPath = this.absPath(filePath);
52
+ await this.ensureDir(path.dirname(fullPath));
53
+ await fs.writeFile(fullPath, data);
54
+ }
55
+
56
+ async remove(targetPath: string | string[]): Promise<string[]> {
57
+ const targets = Array.isArray(targetPath) ? targetPath : [targetPath];
58
+ const removed: string[] = [];
59
+
60
+ for (const item of targets) {
61
+ const fullPath = this.absPath(item);
62
+ try {
63
+ const stat = await fs.stat(fullPath);
64
+ if (stat.isDirectory()) {
65
+ await fs.rm(fullPath, { recursive: true, force: true });
66
+ } else {
67
+ await fs.unlink(fullPath);
68
+ }
69
+ removed.push(fullPath);
70
+ } catch {
71
+ // file doesn't exist or already removed
72
+ }
73
+ }
74
+
75
+ return removed;
76
+ }
77
+
78
+ async copyFile(src: string, dest: string): Promise<void> {
79
+ const srcPath = this.absPath(src);
80
+ const destPath = this.absPath(dest);
81
+ await this.ensureDir(path.dirname(destPath));
82
+ await fs.copyFile(srcPath, destPath);
83
+ }
84
+
85
+ async copyDir(srcDir: string, destDir: string): Promise<void> {
86
+ const src = this.absPath(srcDir);
87
+ const dest = this.absPath(destDir);
88
+ await this.ensureDir(dest);
89
+
90
+ const entries = await fs.readdir(src, { withFileTypes: true });
91
+ for (const entry of entries) {
92
+ const srcItem = path.join(src, entry.name);
93
+ const destItem = path.join(dest, entry.name);
94
+ if (entry.isDirectory()) {
95
+ await this.copyDir(srcItem, destItem);
96
+ } else {
97
+ await this.copyFile(srcItem, destItem);
98
+ }
99
+ }
100
+ }
101
+
102
+ async matchFiles(
103
+ patterns: string | string[],
104
+ options: fg.Options = {}
105
+ ): Promise<string[]> {
106
+ const defaultOptions: fg.Options = {
107
+ cwd: this.workspace,
108
+ absolute: true,
109
+ dot: true,
110
+ onlyFiles: true
111
+ };
112
+ return fg(patterns, { ...defaultOptions, ...options });
113
+ }
114
+
115
+ hash(content: string | Buffer, length = 8): string {
116
+ return crypto.createHash('md5').update(content).digest('hex').slice(0, length);
117
+ }
118
+ }
119
+
120
+ export const file = new FileSystem();
@@ -0,0 +1,63 @@
1
+ import pc from 'picocolors';
2
+
3
+ export enum LogLevel {
4
+ Debug = 0,
5
+ Info = 1,
6
+ Warn = 2,
7
+ Error = 3,
8
+ Silent = 4
9
+ }
10
+
11
+ class Logger {
12
+ level: LogLevel = LogLevel.Info;
13
+
14
+ private formatTag(tag?: string): string {
15
+ return tag ? pc.cyan(`[${tag}]`) : pc.bgBlue(pc.black(' BalmJS '));
16
+ }
17
+
18
+ private timestamp(): string {
19
+ const d = new Date();
20
+ const pad = (n: number) => n.toString().padStart(2, '0');
21
+ return pc.gray(`[${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}]`);
22
+ }
23
+
24
+ debug(tag: string, message: any, ...args: any[]): void {
25
+ if (this.level <= LogLevel.Debug) {
26
+ console.log(this.timestamp(), this.formatTag(tag), pc.magenta('※'), message, ...args);
27
+ }
28
+ }
29
+
30
+ info(tag: string, message: any, ...args: any[]): void {
31
+ if (this.level <= LogLevel.Info) {
32
+ console.log(this.timestamp(), this.formatTag(tag), pc.blue('ℹ'), message, ...args);
33
+ }
34
+ }
35
+
36
+ success(tag: string, message: any, ...args: any[]): void {
37
+ if (this.level <= LogLevel.Info) {
38
+ console.log(this.timestamp(), this.formatTag(tag), pc.green('✔'), pc.green(message), ...args);
39
+ }
40
+ }
41
+
42
+ warn(tag: string, message: any, ...args: any[]): void {
43
+ if (this.level <= LogLevel.Warn) {
44
+ console.warn(this.timestamp(), this.formatTag(tag), pc.yellow('⚠'), pc.yellow(message), ...args);
45
+ }
46
+ }
47
+
48
+ error(tag: string, message: any, ...args: any[]): void {
49
+ if (this.level <= LogLevel.Error) {
50
+ console.error(this.timestamp(), this.formatTag(tag), pc.red('✖'), pc.red(message), ...args);
51
+ }
52
+ }
53
+
54
+ time(label: string): () => void {
55
+ const start = performance.now();
56
+ return () => {
57
+ const duration = (performance.now() - start).toFixed(2);
58
+ this.info('time', `${label} completed in ${pc.bold(duration + 'ms')}`);
59
+ };
60
+ }
61
+ }
62
+
63
+ export const logger = new Logger();
@@ -0,0 +1,46 @@
1
+ export function isObject(val: any): val is Record<string, any> {
2
+ return val !== null && typeof val === 'object' && !Array.isArray(val);
3
+ }
4
+
5
+ export function isArray(val: any): val is any[] {
6
+ return Array.isArray(val);
7
+ }
8
+
9
+ export function isString(val: any): val is string {
10
+ return typeof val === 'string';
11
+ }
12
+
13
+ export function isFunction(val: any): val is Function {
14
+ return typeof val === 'function';
15
+ }
16
+
17
+ export function isBoolean(val: any): val is boolean {
18
+ return typeof val === 'boolean';
19
+ }
20
+
21
+ export function deepMerge<T extends Record<string, any>>(target: T, ...sources: any[]): T {
22
+ if (!sources.length) return target;
23
+ const source = sources.shift();
24
+
25
+ if (isObject(target) && isObject(source)) {
26
+ for (const key in source) {
27
+ if (isObject(source[key])) {
28
+ if (!target[key]) Object.assign(target, { [key]: {} });
29
+ deepMerge(target[key], source[key]);
30
+ } else if (isArray(source[key])) {
31
+ (target as any)[key] = [...source[key]];
32
+ } else if (source[key] !== undefined) {
33
+ Object.assign(target, { [key]: source[key] });
34
+ }
35
+ }
36
+ }
37
+
38
+ return deepMerge(target, ...sources);
39
+ }
40
+
41
+ export function toNamespace(name: string | string[], namespace = 'balm'): string | string[] {
42
+ if (isArray(name)) {
43
+ return name.map((item) => `${namespace}:${item}`);
44
+ }
45
+ return `${namespace}:${name}`;
46
+ }
@@ -0,0 +1,38 @@
1
+ import path from 'node:path';
2
+
3
+ export interface WorkspacePaths {
4
+ local: string;
5
+ global: string;
6
+ }
7
+
8
+ let currentLocalWorkspace: string = process.cwd();
9
+ let currentGlobalWorkspace: string = path.resolve(process.cwd(), '..');
10
+
11
+ export function setWorkspaces(local?: string, global?: string): void {
12
+ if (local) {
13
+ currentLocalWorkspace = path.resolve(local);
14
+ }
15
+ if (global) {
16
+ currentGlobalWorkspace = path.resolve(global);
17
+ } else if (local && !global) {
18
+ currentGlobalWorkspace = path.resolve(currentLocalWorkspace, '..');
19
+ }
20
+ }
21
+
22
+ export function getWorkspaces(): WorkspacePaths {
23
+ return {
24
+ local: currentLocalWorkspace,
25
+ global: currentGlobalWorkspace
26
+ };
27
+ }
28
+
29
+ export function localWorkspace(subPath: string = ''): string {
30
+ return subPath ? path.resolve(currentLocalWorkspace, subPath) : currentLocalWorkspace;
31
+ }
32
+
33
+ export function globalWorkspace(subPath: string = ''): string {
34
+ return subPath ? path.resolve(currentGlobalWorkspace, subPath) : currentGlobalWorkspace;
35
+ }
36
+
37
+ export const localResolve = localWorkspace;
38
+ export const globalResolve = globalWorkspace;