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,77 @@
1
+ import { BaseTask } from './task.js';
2
+ import { BalmConfig } from '../types/index.js';
3
+ import { logger } from '../utilities/logger.js';
4
+ import pc from 'picocolors';
5
+
6
+ export class TaskDAG {
7
+ private tasks = new Map<string, BaseTask>();
8
+
9
+ registerTask(task: BaseTask): void {
10
+ this.tasks.set(task.name, task);
11
+ }
12
+
13
+ getTask(name: string): BaseTask | undefined {
14
+ return this.tasks.get(name);
15
+ }
16
+
17
+ hasTask(name: string): boolean {
18
+ return this.tasks.has(name);
19
+ }
20
+
21
+ clear(): void {
22
+ this.tasks.clear();
23
+ }
24
+
25
+ /**
26
+ * Run a list of tasks in strict series
27
+ */
28
+ async runSeries(taskNames: string[], config: BalmConfig): Promise<void> {
29
+ for (const name of taskNames) {
30
+ await this.executeTask(name, config);
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Run a list of tasks in parallel
36
+ */
37
+ async runParallel(taskNames: string[], config: BalmConfig): Promise<void> {
38
+ await Promise.all(taskNames.map((name) => this.executeTask(name, config)));
39
+ }
40
+
41
+ /**
42
+ * Execute a single task resolving its dependencies first
43
+ */
44
+ async executeTask(
45
+ taskName: string,
46
+ config: BalmConfig,
47
+ executed = new Set<string>()
48
+ ): Promise<void> {
49
+ if (executed.has(taskName)) return;
50
+
51
+ const task = this.tasks.get(taskName);
52
+ if (!task) {
53
+ logger.warn('task', `Task ${pc.bold(taskName)} not found, skipping.`);
54
+ return;
55
+ }
56
+
57
+ // Execute dependencies first
58
+ if (task.deps && task.deps.length > 0) {
59
+ for (const dep of task.deps) {
60
+ await this.executeTask(dep, config, executed);
61
+ }
62
+ }
63
+
64
+ const start = performance.now();
65
+ try {
66
+ await task.run(config);
67
+ executed.add(taskName);
68
+ const duration = (performance.now() - start).toFixed(1);
69
+ if (!task.hidden) {
70
+ logger.info('task', `${pc.cyan(task.name)} finished in ${pc.bold(duration + 'ms')}`);
71
+ }
72
+ } catch (error: any) {
73
+ logger.error('task', `Task ${pc.bold(task.name)} failed: ${error?.message || error}`);
74
+ throw error;
75
+ }
76
+ }
77
+ }
@@ -0,0 +1,38 @@
1
+ import { BalmConfig } from '../types/index.js';
2
+ import { logger } from '../utilities/logger.js';
3
+
4
+ export interface TaskOptions {
5
+ name: string;
6
+ deps?: string[];
7
+ description?: string;
8
+ hidden?: boolean;
9
+ }
10
+
11
+ export abstract class BaseTask {
12
+ name: string;
13
+ deps: string[];
14
+ description?: string;
15
+ hidden: boolean;
16
+
17
+ constructor(options: TaskOptions) {
18
+ this.name = options.name;
19
+ this.deps = options.deps || [];
20
+ this.description = options.description;
21
+ this.hidden = options.hidden || false;
22
+ }
23
+
24
+ abstract run(config: BalmConfig): Promise<void> | void;
25
+ }
26
+
27
+ export class FunctionTask extends BaseTask {
28
+ private fn: (config: BalmConfig) => Promise<void> | void;
29
+
30
+ constructor(name: string, fn: (config: BalmConfig) => Promise<void> | void, deps: string[] = []) {
31
+ super({ name, deps });
32
+ this.fn = fn;
33
+ }
34
+
35
+ async run(config: BalmConfig): Promise<void> {
36
+ await this.fn(config);
37
+ }
38
+ }
package/src/shims.d.ts ADDED
@@ -0,0 +1,5 @@
1
+ declare module 'postcss-preset-env';
2
+ declare module 'connect';
3
+ declare module 'serve-static';
4
+ declare module 'html-minifier-terser';
5
+
@@ -0,0 +1,58 @@
1
+ import path from 'node:path';
2
+ import fg from 'fast-glob';
3
+ import fs from 'node:fs/promises';
4
+ import { BaseTask } from '../runner/task.js';
5
+ import { BalmConfig } from '../types/index.js';
6
+ import { VirtualFile } from '../pipeline/file.js';
7
+ import { AssetRevisioner } from '../pipeline/transforms/cache.js';
8
+ import { file as fsUtil } from '../utilities/file.js';
9
+ import { logger } from '../utilities/logger.js';
10
+
11
+ export class CacheTask extends BaseTask {
12
+ constructor() {
13
+ super({ name: 'cache', description: 'Revision assets and generate manifest' });
14
+ }
15
+
16
+ async run(config: BalmConfig): Promise<void> {
17
+ const destDir = config.dest.base;
18
+ const filesToRev = await fg(['**/*'], {
19
+ cwd: destDir,
20
+ absolute: true,
21
+ onlyFiles: true
22
+ });
23
+
24
+ if (!filesToRev.length) {
25
+ logger.warn('cache', 'No files found to cache');
26
+ return;
27
+ }
28
+
29
+ const virtualFiles = await Promise.all(
30
+ filesToRev.map(async (filePath) => {
31
+ const buffer = await fs.readFile(filePath);
32
+ return new VirtualFile({
33
+ cwd: config.workspace,
34
+ base: destDir,
35
+ path: filePath,
36
+ contents: buffer
37
+ });
38
+ })
39
+ );
40
+
41
+ const revisioner = new AssetRevisioner(config.assets.options);
42
+ const resultFiles = revisioner.process(virtualFiles);
43
+
44
+ // Remove old files that have been renamed
45
+ for (const vFile of resultFiles) {
46
+ if (vFile.revPathOriginal && vFile.revPathOriginal !== vFile.path) {
47
+ await fsUtil.remove(vFile.revPathOriginal);
48
+ }
49
+ }
50
+
51
+ // Write new / updated files
52
+ for (const vFile of resultFiles) {
53
+ await fsUtil.writeFile(vFile.path, vFile.contents);
54
+ }
55
+
56
+ logger.success('cache', 'Asset revisioning and rev-manifest.json completed');
57
+ }
58
+ }
@@ -0,0 +1,21 @@
1
+ import path from 'node:path';
2
+ import { BaseTask } from '../runner/task.js';
3
+ import { BalmConfig } from '../types/index.js';
4
+ import { file } from '../utilities/file.js';
5
+ import { logger } from '../utilities/logger.js';
6
+
7
+ export class CleanTask extends BaseTask {
8
+ constructor() {
9
+ super({ name: 'clean', description: 'Clean output directories' });
10
+ }
11
+
12
+ async run(config: BalmConfig): Promise<void> {
13
+ const targets = [
14
+ path.join(config.workspace, config.roots.tmp),
15
+ path.join(config.workspace, config.roots.target)
16
+ ];
17
+
18
+ const deleted = await file.remove(targets);
19
+ logger.info('clean', `Deleted ${deleted.length} directories`);
20
+ }
21
+ }
@@ -0,0 +1,36 @@
1
+ import path from 'node:path';
2
+ import { BaseTask } from '../runner/task.js';
3
+ import { BalmConfig, RenameOptions } from '../types/index.js';
4
+ import { Pipeline } from '../pipeline/pipeline.js';
5
+ import { transformRename } from '../pipeline/transforms/rename.js';
6
+
7
+ export interface CopyTaskOptions {
8
+ input: string | string[];
9
+ output: string;
10
+ rename?: RenameOptions | string | Function;
11
+ }
12
+
13
+ export class CopyTask extends BaseTask {
14
+ private options: CopyTaskOptions;
15
+
16
+ constructor(options: CopyTaskOptions) {
17
+ super({ name: 'copy', description: 'Copy files to destination' });
18
+ this.options = options;
19
+ }
20
+
21
+ async run(config: BalmConfig): Promise<void> {
22
+ const pipeline = Pipeline.from(this.options.input, {
23
+ cwd: config.workspace
24
+ });
25
+
26
+ if (this.options.rename) {
27
+ pipeline.pipe(transformRename(this.options.rename));
28
+ }
29
+
30
+ const outDir = path.isAbsolute(this.options.output)
31
+ ? this.options.output
32
+ : path.join(config.workspace, this.options.output);
33
+
34
+ await pipeline.dest(outDir);
35
+ }
36
+ }
@@ -0,0 +1,90 @@
1
+ import path from 'node:path';
2
+ import fs from 'node:fs';
3
+ import { Client } from 'ssh2';
4
+ import fg from 'fast-glob';
5
+ import { BaseTask } from '../runner/task.js';
6
+ import { BalmConfig } from '../types/index.js';
7
+ import { logger } from '../utilities/logger.js';
8
+
9
+ export interface FtpTaskOptions {
10
+ localFiles?: string | string[];
11
+ options?: {
12
+ host?: string;
13
+ port?: number;
14
+ username?: string;
15
+ password?: string;
16
+ privateKey?: string;
17
+ remotePath?: string;
18
+ [key: string]: any;
19
+ };
20
+ }
21
+
22
+ export class FtpTask extends BaseTask {
23
+ private ftpOptions: FtpTaskOptions;
24
+
25
+ constructor(ftpOptions: FtpTaskOptions = {}) {
26
+ super({ name: 'ftp', description: 'Upload files via SFTP' });
27
+ this.ftpOptions = ftpOptions;
28
+ }
29
+
30
+ async run(config: BalmConfig): Promise<void> {
31
+ const options = {
32
+ port: 22,
33
+ remotePath: '/',
34
+ ...config.ftp.options,
35
+ ...this.ftpOptions.options
36
+ };
37
+
38
+ if (!options.host) {
39
+ logger.error('ftp', '`host` required for SFTP');
40
+ return;
41
+ }
42
+
43
+ const localFiles = this.ftpOptions.localFiles || config.dest.base;
44
+ const files = await fg(localFiles, { cwd: config.workspace, absolute: true, onlyFiles: true });
45
+
46
+ if (!files.length) {
47
+ logger.warn('ftp', 'No files to upload');
48
+ return;
49
+ }
50
+
51
+ return new Promise((resolve, reject) => {
52
+ const conn = new Client();
53
+
54
+ conn.on('ready', () => {
55
+ conn.sftp((err, sftp) => {
56
+ if (err) {
57
+ conn.end();
58
+ return reject(err);
59
+ }
60
+
61
+ let pending = files.length;
62
+ for (const file of files) {
63
+ const rel = path.relative(config.workspace, file);
64
+ const remoteFile = path.posix.join(options.remotePath, rel);
65
+
66
+ sftp.fastPut(file, remoteFile, (putErr) => {
67
+ if (putErr) {
68
+ logger.error('ftp', `Failed uploading ${file}: ${putErr.message}`);
69
+ } else {
70
+ logger.info('ftp', `Uploaded ${file} -> ${remoteFile}`);
71
+ }
72
+ pending--;
73
+ if (pending === 0) {
74
+ conn.end();
75
+ resolve();
76
+ }
77
+ });
78
+ }
79
+ });
80
+ });
81
+
82
+ conn.on('error', (connErr) => {
83
+ logger.error('ftp', connErr.message);
84
+ reject(connErr);
85
+ });
86
+
87
+ conn.connect(options as any);
88
+ });
89
+ }
90
+ }
@@ -0,0 +1,78 @@
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 { transformHtmlmin } from '../pipeline/transforms/htmlmin.js';
6
+ import { transformReplace } from '../pipeline/transforms/replace.js';
7
+ import { PUBLIC_URL } from '../config/constants.js';
8
+
9
+ const LIVE_RELOAD_SCRIPT = `
10
+ <!-- BalmJS Live Reload -->
11
+ <script>
12
+ (() => {
13
+ const protocol = location.protocol === 'https:' ? 'wss:' : 'ws:';
14
+ const ws = new WebSocket(\`\${protocol}//\${location.host}/__balm_ws\`);
15
+ ws.onmessage = (e) => {
16
+ try {
17
+ const msg = JSON.parse(e.data);
18
+ if (msg.type === 'reload') {
19
+ console.log('[BalmJS] Reloading page...');
20
+ location.reload();
21
+ } else if (msg.type === 'reload-css') {
22
+ console.log('[BalmJS] Reloading CSS...');
23
+ const links = document.querySelectorAll('link[rel="stylesheet"]');
24
+ for (const link of links) {
25
+ const url = new URL(link.href);
26
+ url.searchParams.set('_balm_t', Date.now().toString());
27
+ link.href = url.href;
28
+ }
29
+ }
30
+ } catch {}
31
+ };
32
+ ws.onclose = () => {
33
+ setTimeout(() => location.reload(), 2000);
34
+ };
35
+ })();
36
+ </script>
37
+ `;
38
+
39
+ export class HtmlTask extends BaseTask {
40
+ constructor() {
41
+ super({ name: 'html', description: 'Process HTML templates' });
42
+ }
43
+
44
+ async run(config: BalmConfig): Promise<void> {
45
+ const templateDir = config.scripts.injectHtml
46
+ ? config.src.base
47
+ : config.src.html;
48
+
49
+ const pattern = path.join(templateDir, '*.html');
50
+ const pipeline = Pipeline.from(pattern, {
51
+ cwd: config.workspace,
52
+ base: templateDir
53
+ });
54
+
55
+ // Replace %PUBLIC_URL% placeholder
56
+ const publicUrlReplacement = config.assets.virtualDir ? `/${config.assets.virtualDir}` : '';
57
+ pipeline.pipe(transformReplace(new RegExp(PUBLIC_URL, 'g'), publicUrlReplacement));
58
+
59
+ if (config.env.isProd) {
60
+ pipeline.pipe(transformHtmlmin(config.html.options));
61
+ } else {
62
+ pipeline.pipe(async (file) => {
63
+ if (/\.html$/i.test(file.extname)) {
64
+ let content = file.toString();
65
+ if (content.includes('</body>')) {
66
+ content = content.replace('</body>', `${LIVE_RELOAD_SCRIPT}\n</body>`);
67
+ } else {
68
+ content += `\n${LIVE_RELOAD_SCRIPT}`;
69
+ }
70
+ file.contents = Buffer.from(content);
71
+ }
72
+ return file;
73
+ });
74
+ }
75
+
76
+ await pipeline.dest(config.dest.html);
77
+ }
78
+ }
@@ -0,0 +1,13 @@
1
+ export * from './clean.js';
2
+ export * from './style.js';
3
+ export * from './script.js';
4
+ export * from './html.js';
5
+ export * from './copy.js';
6
+ export * from './remove.js';
7
+ export * from './replace.js';
8
+ export * from './cache.js';
9
+ export * from './publish.js';
10
+ export * from './pwa.js';
11
+ export * from './zip.js';
12
+ export * from './ftp.js';
13
+ export * from './server.js';
@@ -0,0 +1,71 @@
1
+ import path from 'node:path';
2
+ import { BaseTask } from '../runner/task.js';
3
+ import { BalmConfig, TemplateOption, RenameOptions } from '../types/index.js';
4
+ import { Pipeline } from '../pipeline/pipeline.js';
5
+ import { transformRename } from '../pipeline/transforms/rename.js';
6
+ import { logger } from '../utilities/logger.js';
7
+
8
+ export interface PublishTaskOptions {
9
+ input?: string | TemplateOption[];
10
+ output?: string;
11
+ renameOptions?: string | Function | RenameOptions;
12
+ }
13
+
14
+ export class PublishTask extends BaseTask {
15
+ private options: PublishTaskOptions;
16
+
17
+ constructor(options: PublishTaskOptions = {}) {
18
+ super({ name: 'publish', description: 'Publish assets or templates' });
19
+ this.options = options;
20
+ }
21
+
22
+ async run(config: BalmConfig): Promise<void> {
23
+ if (!config.env.isProd) {
24
+ logger.warn('publish', '`mix.publish()` is only supported in production mode');
25
+ return;
26
+ }
27
+
28
+ const { input, output, renameOptions } = this.options;
29
+
30
+ if (Array.isArray(input)) {
31
+ for (const item of input) {
32
+ await this.publishItem(config, item.input, item.output, item.renameOptions);
33
+ }
34
+ } else if (typeof input === 'string' && output) {
35
+ await this.publishItem(config, input, output, renameOptions);
36
+ } else {
37
+ // Default: publish all static assets from dest.base to assets.static
38
+ const srcDir = config.dest.base;
39
+ const targetDir = config.assets.static || path.join(config.assets.root, config.assets.mainDir);
40
+
41
+ const pipeline = Pipeline.from(['**/*'], {
42
+ cwd: srcDir,
43
+ base: srcDir
44
+ });
45
+
46
+ await pipeline.dest(path.resolve(config.workspace, targetDir));
47
+ }
48
+ }
49
+
50
+ private async publishItem(
51
+ config: BalmConfig,
52
+ input: string,
53
+ output: string,
54
+ rename?: string | Function | RenameOptions
55
+ ): Promise<void> {
56
+ const srcPath = path.isAbsolute(input) ? input : path.join(config.dest.base, input);
57
+ const destDir = path.isAbsolute(output)
58
+ ? output
59
+ : path.join(config.workspace, config.assets.root, output);
60
+
61
+ const pipeline = Pipeline.from(srcPath, {
62
+ cwd: config.dest.base
63
+ });
64
+
65
+ if (rename) {
66
+ pipeline.pipe(transformRename(rename));
67
+ }
68
+
69
+ await pipeline.dest(destDir);
70
+ }
71
+ }
@@ -0,0 +1,49 @@
1
+ import path from 'node:path';
2
+ import { generateSW, injectManifest } from 'workbox-build';
3
+ import { BaseTask } from '../runner/task.js';
4
+ import { BalmConfig } from '../types/index.js';
5
+ import { logger } from '../utilities/logger.js';
6
+
7
+ export interface PwaTaskOptions {
8
+ mode?: 'generateSW' | 'injectManifest';
9
+ options?: Record<string, any>;
10
+ }
11
+
12
+ export class PwaTask extends BaseTask {
13
+ private options: PwaTaskOptions;
14
+
15
+ constructor(options: PwaTaskOptions = {}) {
16
+ super({ name: 'pwa', description: 'Generate Progressive Web App Service Worker' });
17
+ this.options = options;
18
+ }
19
+
20
+ async run(config: BalmConfig): Promise<void> {
21
+ const mode = this.options.mode || config.pwa.mode || 'generateSW';
22
+ const globDirectory = config.dest.base;
23
+ const swDest = path.join(globDirectory, config.pwa.swDestFilename);
24
+ const swSrc = path.join(config.src.base, config.pwa.swSrcFilename);
25
+
26
+ const mergedOptions: any = {
27
+ globDirectory,
28
+ swDest,
29
+ ...config.pwa.options,
30
+ ...this.options.options
31
+ };
32
+
33
+ if (mode === 'injectManifest') {
34
+ mergedOptions.swSrc = mergedOptions.swSrc || swSrc;
35
+ }
36
+
37
+ try {
38
+ if (mode === 'generateSW') {
39
+ const { count, size } = await generateSW(mergedOptions);
40
+ logger.info('pwa', `Generated '${swDest}', precaching ${count} files (${size} bytes)`);
41
+ } else {
42
+ const { count, size } = await injectManifest(mergedOptions);
43
+ logger.info('pwa', `Injected manifest into '${swDest}', precaching ${count} files (${size} bytes)`);
44
+ }
45
+ } catch (err: any) {
46
+ logger.warn('pwa', `Service worker generation failed: ${err.message || err}`);
47
+ }
48
+ }
49
+ }
@@ -0,0 +1,24 @@
1
+ import path from 'node:path';
2
+ import { BaseTask } from '../runner/task.js';
3
+ import { BalmConfig } from '../types/index.js';
4
+ import { file } from '../utilities/file.js';
5
+ import { logger } from '../utilities/logger.js';
6
+
7
+ export class RemoveTask extends BaseTask {
8
+ private target: string | string[];
9
+
10
+ constructor(target: string | string[]) {
11
+ super({ name: 'remove', description: 'Remove files and directories' });
12
+ this.target = target;
13
+ }
14
+
15
+ async run(config: BalmConfig): Promise<void> {
16
+ const targets = Array.isArray(this.target) ? this.target : [this.target];
17
+ const resolvedTargets = targets.map((t) =>
18
+ path.isAbsolute(t) ? t : path.join(config.workspace, t)
19
+ );
20
+
21
+ const deleted = await file.remove(resolvedTargets);
22
+ logger.warn('remove', `Removed: ${deleted.join(', ')}`);
23
+ }
24
+ }
@@ -0,0 +1,34 @@
1
+ import path from 'node:path';
2
+ import { BaseTask } from '../runner/task.js';
3
+ import { BalmConfig, ReplaceOptions } from '../types/index.js';
4
+ import { Pipeline } from '../pipeline/pipeline.js';
5
+ import { transformReplace } from '../pipeline/transforms/replace.js';
6
+
7
+ export interface ReplaceTaskOptions {
8
+ input: string | string[];
9
+ output: string;
10
+ options: ReplaceOptions | ReplaceOptions[];
11
+ }
12
+
13
+ export class ReplaceTask extends BaseTask {
14
+ private taskOptions: ReplaceTaskOptions;
15
+
16
+ constructor(taskOptions: ReplaceTaskOptions) {
17
+ super({ name: 'replace', description: 'Replace content in files' });
18
+ this.taskOptions = taskOptions;
19
+ }
20
+
21
+ async run(config: BalmConfig): Promise<void> {
22
+ const pipeline = Pipeline.from(this.taskOptions.input, {
23
+ cwd: config.workspace
24
+ });
25
+
26
+ pipeline.pipe(transformReplace(this.taskOptions.options));
27
+
28
+ const outDir = path.isAbsolute(this.taskOptions.output)
29
+ ? this.taskOptions.output
30
+ : path.join(config.workspace, this.taskOptions.output);
31
+
32
+ await pipeline.dest(outDir);
33
+ }
34
+ }
@@ -0,0 +1,28 @@
1
+ import { BaseTask } from '../runner/task.js';
2
+ import { BalmConfig, BundlerType } from '../types/index.js';
3
+ import { runWebpack } from '../bundler/webpack.js';
4
+ import { runRollup } from '../bundler/rollup.js';
5
+ import { runEsbuild } from '../bundler/esbuild.js';
6
+
7
+ export class ScriptTask extends BaseTask {
8
+ constructor() {
9
+ super({ name: 'script', description: 'Bundle JavaScript/TypeScript' });
10
+ }
11
+
12
+ async run(config: BalmConfig): Promise<void> {
13
+ const bundler = config.scripts.bundler;
14
+
15
+ switch (bundler) {
16
+ case BundlerType.rollup:
17
+ await runRollup(config);
18
+ break;
19
+ case BundlerType.esbuild:
20
+ await runEsbuild(config);
21
+ break;
22
+ case BundlerType.webpack:
23
+ default:
24
+ await runWebpack(config);
25
+ break;
26
+ }
27
+ }
28
+ }