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
@@ -0,0 +1,104 @@
1
+ import path from 'node:path';
2
+ import { existsSync } from 'node:fs';
3
+ import { BaseTask } from '../runner/task.js';
4
+ import { BalmConfig } from '../types/index.js';
5
+ import { Pipeline } from '../pipeline/pipeline.js';
6
+ import { PUBLIC_URL } from '../config/constants.js';
7
+
8
+ export class StaticTask extends BaseTask {
9
+ constructor() {
10
+ super({ name: 'static', description: 'Copy static assets, images, fonts, and manifest' });
11
+ }
12
+
13
+ async run(config: BalmConfig): Promise<void> {
14
+ const srcBase = config.src.base;
15
+ const destBase = config.dest.base;
16
+
17
+ // 1. Copy root static files (favicon.ico, robots.txt, icons, etc.)
18
+ const staticPatterns = [
19
+ path.join(srcBase, '*.{ico,png,svg,json,txt,xml,webmanifest}'),
20
+ `!${path.join(srcBase, '*.html')}`
21
+ ];
22
+
23
+ const pipeline = Pipeline.from(staticPatterns, {
24
+ cwd: config.workspace,
25
+ base: srcBase
26
+ });
27
+
28
+ const publicUrlReplacement = config.assets.virtualDir ? `/${config.assets.virtualDir}` : '';
29
+ pipeline.pipe(async (file) => {
30
+ if (file.basename === 'manifest.json' || file.extname === '.json') {
31
+ let content = file.toString();
32
+ content = content.replaceAll(PUBLIC_URL, publicUrlReplacement);
33
+ file.contents = Buffer.from(content);
34
+ }
35
+ return file;
36
+ });
37
+
38
+ await pipeline.dest(destBase);
39
+
40
+ // 2. Copy images folder if exists in src
41
+ if (config.src.img && existsSync(config.src.img)) {
42
+ const imgPipeline = Pipeline.from(path.join(config.src.img, '**/*'), {
43
+ cwd: config.workspace,
44
+ base: config.src.img
45
+ });
46
+ await imgPipeline.dest(config.dest.img);
47
+
48
+ // If source and target img dirs differ (e.g. images vs img), also copy to source dir for compatibility
49
+ if (config.paths.source.img && config.paths.target.img && config.paths.source.img !== config.paths.target.img) {
50
+ const compatPipeline = Pipeline.from(path.join(config.src.img, '**/*'), {
51
+ cwd: config.workspace,
52
+ base: config.src.img
53
+ });
54
+ await compatPipeline.dest(path.join(destBase, config.paths.source.img));
55
+ }
56
+ }
57
+
58
+ // 3. Copy fonts folder if exists in src
59
+ if (config.src.font && existsSync(config.src.font)) {
60
+ const fontPipeline = Pipeline.from(path.join(config.src.font, '**/*'), {
61
+ cwd: config.workspace,
62
+ base: config.src.font
63
+ });
64
+ await fontPipeline.dest(config.dest.font);
65
+
66
+ // Ensure both 'font' and 'fonts' directories exist in dest for compatibility
67
+ const targetFontsDir = path.join(destBase, 'fonts');
68
+ if (config.dest.font !== targetFontsDir) {
69
+ const fontsPipeline = Pipeline.from(path.join(config.src.font, '**/*'), {
70
+ cwd: config.workspace,
71
+ base: config.src.font
72
+ });
73
+ await fontsPipeline.dest(targetFontsDir);
74
+ }
75
+ const targetFontDir = path.join(destBase, 'font');
76
+ if (config.dest.font !== targetFontDir) {
77
+ const fontCompatPipeline = Pipeline.from(path.join(config.src.font, '**/*'), {
78
+ cwd: config.workspace,
79
+ base: config.src.font
80
+ });
81
+ await fontCompatPipeline.dest(targetFontDir);
82
+ }
83
+ }
84
+
85
+ // 4. Copy media folder if exists in src
86
+ if (config.src.media && existsSync(config.src.media)) {
87
+ const mediaPipeline = Pipeline.from(path.join(config.src.media, '**/*'), {
88
+ cwd: config.workspace,
89
+ base: config.src.media
90
+ });
91
+ await mediaPipeline.dest(config.dest.media);
92
+ }
93
+
94
+ // 5. Copy public folder if exists in workspace
95
+ const publicDir = path.join(config.workspace, 'public');
96
+ if (existsSync(publicDir)) {
97
+ const pubPipeline = Pipeline.from(path.join(publicDir, '**/*'), {
98
+ cwd: config.workspace,
99
+ base: publicDir
100
+ });
101
+ await pubPipeline.dest(destBase);
102
+ }
103
+ }
104
+ }
@@ -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,202 @@
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
+ htmlPluginOptions?: Record<string, any>;
62
+ alias: Record<string, string>;
63
+ extensions?: string[];
64
+ plugins: any[];
65
+ loaders: any[];
66
+ defaultLoaders: {
67
+ html?: boolean;
68
+ css?: boolean;
69
+ js?: boolean;
70
+ url?: boolean;
71
+ };
72
+ options: Record<string, any>;
73
+ esbuildOptions: Record<string, any>;
74
+ rollupOptions: Record<string, any>;
75
+ webpackOptions: Record<string, any>;
76
+ useCache: boolean;
77
+ }
78
+
79
+ export interface BalmHtml {
80
+ options: Record<string, any>;
81
+ }
82
+
83
+ export interface BalmAssets {
84
+ root: string;
85
+ mainDir: string;
86
+ subDir: string;
87
+ buildDir: string;
88
+ virtualDir: string;
89
+ cache: boolean;
90
+ options: {
91
+ fileNameManifest?: string;
92
+ dontRenameFile?: (string | RegExp)[];
93
+ dontUpdateReference?: (string | RegExp)[];
94
+ hashLength?: number;
95
+ [key: string]: any;
96
+ };
97
+ includes: string[];
98
+ excludes: string[];
99
+ static?: string;
100
+ [key: string]: any;
101
+ }
102
+
103
+ export interface BalmServer {
104
+ host: string | null;
105
+ port: number;
106
+ open: boolean | string;
107
+ proxy: boolean | Record<string, any> | Array<Record<string, any>>;
108
+ proxyOptions: boolean | Record<string, any> | Array<Record<string, any>>;
109
+ historyOptions: boolean | Record<string, any>;
110
+ useHMR: boolean;
111
+ options: Record<string, any>;
112
+ }
113
+
114
+ export interface BalmPwa {
115
+ enabled: boolean;
116
+ mode: 'generateSW' | 'injectManifest';
117
+ version: string;
118
+ manifest: string;
119
+ swSrcFilename: string;
120
+ swDestFilename: string;
121
+ options: Record<string, any>;
122
+ }
123
+
124
+ export interface BalmFtp {
125
+ options: Record<string, any>;
126
+ watchFiles: string[];
127
+ }
128
+
129
+ export interface BalmImages {
130
+ plugins: any[];
131
+ }
132
+
133
+ export interface BalmEnv {
134
+ isProd: boolean;
135
+ isDev: boolean;
136
+ isTest: boolean;
137
+ inFrontend: boolean;
138
+ inSSR: boolean;
139
+ inDesktopApp: boolean;
140
+ isMP: boolean;
141
+ }
142
+
143
+ export interface BalmWorkspaces {
144
+ local: string;
145
+ global: string;
146
+ }
147
+
148
+ export type BalmWorkspaceConfig = string | { local?: string; global?: string };
149
+
150
+ export interface BalmConfig {
151
+ workspace: string;
152
+ workspaces: BalmWorkspaces;
153
+ alias: Record<string, string>;
154
+ env: BalmEnv;
155
+ inFrontend: boolean;
156
+ useDefaults: boolean;
157
+ roots: BalmRoots;
158
+ paths: BalmPaths;
159
+ styles: BalmStyles;
160
+ scripts: BalmScripts;
161
+ html: BalmHtml;
162
+ assets: BalmAssets;
163
+ server: BalmServer;
164
+ pwa: BalmPwa;
165
+ ftp: BalmFtp;
166
+ images: BalmImages;
167
+ logs: {
168
+ level: number;
169
+ };
170
+ src: BalmPathMap;
171
+ dest: BalmPathMap & { static: string };
172
+ [key: string]: any;
173
+ }
174
+
175
+ export type BalmCustomConfig = Omit<DeepPartial<BalmConfig>, 'workspace'> & {
176
+ workspace?: BalmWorkspaceConfig;
177
+ };
178
+
179
+ export interface RenameOptions {
180
+ dirname?: string;
181
+ prefix?: string;
182
+ basename?: string;
183
+ suffix?: string;
184
+ extname?: string;
185
+ }
186
+
187
+ export interface ReplaceOptions {
188
+ substr: string | RegExp;
189
+ replacement: string | ((substring: string, ...args: any[]) => string);
190
+ }
191
+
192
+ export interface TemplateOption {
193
+ input: string;
194
+ output: string;
195
+ renameOptions?: string | Function | RenameOptions;
196
+ }
197
+
198
+ export interface TaskContext {
199
+ config: BalmConfig;
200
+ }
201
+
202
+ 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;