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.
- package/CHANGELOG.md +16 -0
- package/dist/index.cjs +2625 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +569 -0
- package/dist/index.d.ts +569 -0
- package/dist/index.js +2527 -0
- package/dist/index.js.map +1 -0
- package/package.json +49 -123
- package/src/balm.ts +108 -0
- package/src/bundler/esbuild.ts +47 -0
- package/src/bundler/index.ts +3 -0
- package/src/bundler/rollup.ts +45 -0
- package/src/bundler/webpack.ts +124 -0
- package/src/config/constants.ts +12 -0
- package/src/config/defaults.ts +209 -0
- package/src/config/index.ts +112 -0
- package/src/hooks/index.ts +224 -0
- package/src/index.ts +16 -0
- package/src/pipeline/file.ts +69 -0
- package/src/pipeline/index.ts +10 -0
- package/src/pipeline/pipeline.ts +135 -0
- package/src/pipeline/transforms/cache.ts +146 -0
- package/src/pipeline/transforms/htmlmin.ts +18 -0
- package/src/pipeline/transforms/less.ts +75 -0
- package/src/pipeline/transforms/postcss.ts +43 -0
- package/src/pipeline/transforms/rename.ts +37 -0
- package/src/pipeline/transforms/replace.ts +30 -0
- package/src/pipeline/transforms/sass.ts +106 -0
- package/src/pipeline/transforms/terser.ts +20 -0
- package/src/runner/dag.ts +77 -0
- package/src/runner/task.ts +38 -0
- package/src/shims.d.ts +5 -0
- package/src/tasks/cache.ts +72 -0
- package/src/tasks/clean.ts +21 -0
- package/src/tasks/copy.ts +36 -0
- package/src/tasks/ftp.ts +90 -0
- package/src/tasks/html.ts +232 -0
- package/src/tasks/index.ts +14 -0
- package/src/tasks/publish.ts +71 -0
- package/src/tasks/pwa.ts +99 -0
- package/src/tasks/remove.ts +24 -0
- package/src/tasks/replace.ts +34 -0
- package/src/tasks/script.ts +28 -0
- package/src/tasks/server.ts +183 -0
- package/src/tasks/static.ts +104 -0
- package/src/tasks/style.ts +57 -0
- package/src/tasks/zip.ts +65 -0
- package/src/types/index.ts +202 -0
- package/src/utilities/file.ts +120 -0
- package/src/utilities/logger.ts +63 -0
- package/src/utilities/utils.ts +46 -0
- package/src/utilities/workspace.ts +38 -0
- package/src/watcher/index.ts +100 -0
- package/tsconfig.json +8 -0
- package/tsup.config.ts +12 -0
- package/README.md +0 -196
- package/index.d.ts +0 -360
- package/lib/balm.js +0 -49
- package/lib/bootstrap/check_config.js +0 -43
- package/lib/bootstrap/index.js +0 -87
- package/lib/bundler/esbuild/build.js +0 -26
- package/lib/bundler/esbuild/index.js +0 -7
- package/lib/bundler/esbuild/options.js +0 -7
- package/lib/bundler/esbuild/transform.js +0 -36
- package/lib/bundler/rollup/build.js +0 -52
- package/lib/bundler/rollup/entry.js +0 -28
- package/lib/bundler/rollup/index.js +0 -20
- package/lib/bundler/rollup/output.js +0 -20
- package/lib/bundler/rollup/plugins.js +0 -31
- package/lib/bundler/webpack/config/common.js +0 -135
- package/lib/bundler/webpack/config/dev.js +0 -12
- package/lib/bundler/webpack/config/env.js +0 -67
- package/lib/bundler/webpack/config/index.js +0 -6
- package/lib/bundler/webpack/config/prod.js +0 -39
- package/lib/bundler/webpack/config/regex.js +0 -10
- package/lib/bundler/webpack/entry.js +0 -65
- package/lib/bundler/webpack/index.js +0 -32
- package/lib/bundler/webpack/loaders.js +0 -50
- package/lib/bundler/webpack/output.js +0 -41
- package/lib/bundler/webpack/rules/asset.js +0 -33
- package/lib/bundler/webpack/rules/babel.js +0 -19
- package/lib/bundler/webpack/rules/css.js +0 -109
- package/lib/bundler/webpack/rules/html.js +0 -15
- package/lib/bundler/webpack/rules/index.js +0 -10
- package/lib/config/assets.js +0 -30
- package/lib/config/constants.js +0 -14
- package/lib/config/env.js +0 -35
- package/lib/config/extras.js +0 -6
- package/lib/config/ftp.js +0 -6
- package/lib/config/globals.js +0 -43
- package/lib/config/html.js +0 -22
- package/lib/config/images.js +0 -4
- package/lib/config/index.js +0 -63
- package/lib/config/logs.js +0 -11
- package/lib/config/paths.js +0 -46
- package/lib/config/pwa.js +0 -24
- package/lib/config/roots.js +0 -9
- package/lib/config/scripts.js +0 -162
- package/lib/config/server.js +0 -84
- package/lib/config/spinner.js +0 -9
- package/lib/config/styles.js +0 -67
- package/lib/hello.js +0 -19
- package/lib/hooks/index.js +0 -94
- package/lib/hooks/maker.js +0 -59
- package/lib/index.js +0 -5
- package/lib/middlewares/history.js +0 -6
- package/lib/middlewares/index.js +0 -11
- package/lib/middlewares/proxy.js +0 -28
- package/lib/middlewares/webpack.js +0 -26
- package/lib/plugins/htmlmin.js +0 -39
- package/lib/plugins/imagemin.js +0 -95
- package/lib/plugins/index.js +0 -27
- package/lib/plugins/jsmin.js +0 -37
- package/lib/plugins/less.js +0 -97
- package/lib/plugins/plumber.js +0 -87
- package/lib/plugins/postcss-plugins.js +0 -13
- package/lib/plugins/postcss.js +0 -117
- package/lib/plugins/rename.js +0 -51
- package/lib/plugins/replace.js +0 -91
- package/lib/plugins/sass-legacy.js +0 -131
- package/lib/plugins/sass.js +0 -134
- package/lib/plugins/sftp.js +0 -288
- package/lib/tasks/default.js +0 -46
- package/lib/tasks/foundation/balm.js +0 -72
- package/lib/tasks/foundation/balm_style.js +0 -65
- package/lib/tasks/foundation/index.js +0 -10
- package/lib/tasks/index.js +0 -48
- package/lib/tasks/private/build.js +0 -18
- package/lib/tasks/private/cache.js +0 -31
- package/lib/tasks/private/clean.js +0 -69
- package/lib/tasks/private/end.js +0 -24
- package/lib/tasks/private/extra.js +0 -35
- package/lib/tasks/private/font.js +0 -15
- package/lib/tasks/private/image.js +0 -37
- package/lib/tasks/private/index.js +0 -34
- package/lib/tasks/private/lint.js +0 -20
- package/lib/tasks/private/media.js +0 -15
- package/lib/tasks/private/modernizr.js +0 -59
- package/lib/tasks/private/pwa-cache.js +0 -32
- package/lib/tasks/private/script.js +0 -9
- package/lib/tasks/private/sprite.js +0 -105
- package/lib/tasks/private/start.js +0 -16
- package/lib/tasks/private/style.js +0 -9
- package/lib/tasks/private/workbox-sw.js +0 -15
- package/lib/tasks/public/copy.js +0 -16
- package/lib/tasks/public/esbuild.js +0 -18
- package/lib/tasks/public/ftp.js +0 -39
- package/lib/tasks/public/html.js +0 -65
- package/lib/tasks/public/index.js +0 -38
- package/lib/tasks/public/jsmin.js +0 -23
- package/lib/tasks/public/less.js +0 -21
- package/lib/tasks/public/postcss.js +0 -16
- package/lib/tasks/public/publish.js +0 -51
- package/lib/tasks/public/pwa.js +0 -84
- package/lib/tasks/public/remove.js +0 -46
- package/lib/tasks/public/replace.js +0 -26
- package/lib/tasks/public/rollup.js +0 -17
- package/lib/tasks/public/sass.js +0 -27
- package/lib/tasks/public/server.js +0 -130
- package/lib/tasks/public/url.js +0 -35
- package/lib/tasks/public/version.js +0 -23
- package/lib/tasks/public/webpack.js +0 -39
- package/lib/tasks/public/zip.js +0 -19
- package/lib/utilities/address.js +0 -55
- package/lib/utilities/cache-core.js +0 -66
- package/lib/utilities/color.js +0 -27
- package/lib/utilities/compiling.js +0 -24
- package/lib/utilities/detect-port.js +0 -100
- package/lib/utilities/escape-string-regexp.js +0 -10
- package/lib/utilities/file.js +0 -70
- package/lib/utilities/index.js +0 -8
- package/lib/utilities/loading.js +0 -37
- package/lib/utilities/logger.js +0 -99
- package/lib/utilities/merge-stream.js +0 -38
- package/lib/utilities/namespace.js +0 -11
- package/lib/utilities/public-url.js +0 -37
- package/lib/utilities/replacestream.js +0 -106
- package/lib/utilities/typeof.js +0 -5
- package/lib/utilities/utils.js +0 -43
- package/tslib/balm.js +0 -44
- package/tslib/bootstrap/check_config.js +0 -36
- package/tslib/bootstrap/index.js +0 -82
- package/tslib/bundler/esbuild/build.js +0 -30
- package/tslib/bundler/esbuild/index.js +0 -11
- package/tslib/bundler/esbuild/options.js +0 -7
- package/tslib/bundler/esbuild/transform.js +0 -38
- package/tslib/bundler/rollup/build.js +0 -17
- package/tslib/bundler/rollup/entry.js +0 -24
- package/tslib/bundler/rollup/index.js +0 -21
- package/tslib/bundler/rollup/output.js +0 -23
- package/tslib/bundler/rollup/plugins.js +0 -27
- package/tslib/bundler/webpack/config/common.js +0 -130
- package/tslib/bundler/webpack/config/dev.js +0 -14
- package/tslib/bundler/webpack/config/env.js +0 -44
- package/tslib/bundler/webpack/config/index.js +0 -8
- package/tslib/bundler/webpack/config/prod.js +0 -43
- package/tslib/bundler/webpack/config/regex.js +0 -10
- package/tslib/bundler/webpack/entry.js +0 -68
- package/tslib/bundler/webpack/index.js +0 -30
- package/tslib/bundler/webpack/loaders.js +0 -51
- package/tslib/bundler/webpack/output.js +0 -41
- package/tslib/bundler/webpack/rules/asset.js +0 -33
- package/tslib/bundler/webpack/rules/babel.js +0 -16
- package/tslib/bundler/webpack/rules/css.js +0 -95
- package/tslib/bundler/webpack/rules/html.js +0 -15
- package/tslib/bundler/webpack/rules/index.js +0 -10
- package/tslib/config/assets.js +0 -24
- package/tslib/config/constants.js +0 -14
- package/tslib/config/env.js +0 -43
- package/tslib/config/extras.js +0 -6
- package/tslib/config/ftp.js +0 -6
- package/tslib/config/globals.js +0 -47
- package/tslib/config/html.js +0 -17
- package/tslib/config/images.js +0 -4
- package/tslib/config/index.js +0 -56
- package/tslib/config/logs.js +0 -6
- package/tslib/config/paths.js +0 -30
- package/tslib/config/pwa.js +0 -18
- package/tslib/config/roots.js +0 -8
- package/tslib/config/scripts.js +0 -93
- package/tslib/config/server.js +0 -33
- package/tslib/config/spinner.js +0 -10
- package/tslib/config/styles.js +0 -37
- package/tslib/hello.js +0 -19
- package/tslib/hooks/index.js +0 -75
- package/tslib/hooks/maker.js +0 -66
- package/tslib/index.js +0 -5
- package/tslib/middlewares/history.js +0 -12
- package/tslib/middlewares/index.js +0 -18
- package/tslib/middlewares/proxy.js +0 -27
- package/tslib/middlewares/webpack.js +0 -29
- package/tslib/plugins/htmlmin.js +0 -38
- package/tslib/plugins/imagemin.js +0 -97
- package/tslib/plugins/index.js +0 -27
- package/tslib/plugins/jsmin.js +0 -39
- package/tslib/plugins/less.js +0 -90
- package/tslib/plugins/plumber.js +0 -81
- package/tslib/plugins/postcss-plugins.js +0 -24
- package/tslib/plugins/postcss.js +0 -111
- package/tslib/plugins/rename.js +0 -51
- package/tslib/plugins/replace.js +0 -72
- package/tslib/plugins/sass-legacy.js +0 -95
- package/tslib/plugins/sass.js +0 -109
- package/tslib/plugins/sftp.js +0 -258
- package/tslib/tasks/default.js +0 -59
- package/tslib/tasks/foundation/balm.js +0 -75
- package/tslib/tasks/foundation/balm_style.js +0 -68
- package/tslib/tasks/foundation/index.js +0 -10
- package/tslib/tasks/index.js +0 -43
- package/tslib/tasks/private/build.js +0 -15
- package/tslib/tasks/private/cache.js +0 -48
- package/tslib/tasks/private/clean.js +0 -82
- package/tslib/tasks/private/end.js +0 -21
- package/tslib/tasks/private/extra.js +0 -34
- package/tslib/tasks/private/font.js +0 -12
- package/tslib/tasks/private/image.js +0 -42
- package/tslib/tasks/private/index.js +0 -34
- package/tslib/tasks/private/lint.js +0 -17
- package/tslib/tasks/private/media.js +0 -12
- package/tslib/tasks/private/modernizr.js +0 -54
- package/tslib/tasks/private/pwa-cache.js +0 -32
- package/tslib/tasks/private/script.js +0 -9
- package/tslib/tasks/private/sprite.js +0 -93
- package/tslib/tasks/private/start.js +0 -13
- package/tslib/tasks/private/style.js +0 -12
- package/tslib/tasks/private/workbox-sw.js +0 -15
- package/tslib/tasks/public/copy.js +0 -18
- package/tslib/tasks/public/esbuild.js +0 -18
- package/tslib/tasks/public/ftp.js +0 -39
- package/tslib/tasks/public/html.js +0 -75
- package/tslib/tasks/public/index.js +0 -38
- package/tslib/tasks/public/jsmin.js +0 -24
- package/tslib/tasks/public/less.js +0 -21
- package/tslib/tasks/public/postcss.js +0 -16
- package/tslib/tasks/public/publish.js +0 -50
- package/tslib/tasks/public/pwa.js +0 -87
- package/tslib/tasks/public/remove.js +0 -44
- package/tslib/tasks/public/replace.js +0 -27
- package/tslib/tasks/public/rollup.js +0 -17
- package/tslib/tasks/public/sass.js +0 -27
- package/tslib/tasks/public/server.js +0 -129
- package/tslib/tasks/public/url.js +0 -35
- package/tslib/tasks/public/version.js +0 -27
- package/tslib/tasks/public/webpack.js +0 -41
- package/tslib/tasks/public/zip.js +0 -19
- package/tslib/utilities/address.js +0 -48
- package/tslib/utilities/cache-core.js +0 -57
- package/tslib/utilities/color.js +0 -33
- package/tslib/utilities/compiling.js +0 -16
- package/tslib/utilities/detect-port.js +0 -91
- package/tslib/utilities/escape-string-regexp.js +0 -7
- package/tslib/utilities/file.js +0 -78
- package/tslib/utilities/index.js +0 -8
- package/tslib/utilities/loading.js +0 -25
- package/tslib/utilities/logger.js +0 -92
- package/tslib/utilities/merge-stream.js +0 -34
- package/tslib/utilities/namespace.js +0 -12
- package/tslib/utilities/public-url.js +0 -31
- package/tslib/utilities/replacestream.js +0 -112
- package/tslib/utilities/typeof.js +0 -8
- package/tslib/utilities/utils.js +0 -38
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import postcss, { AcceptedPlugin } from 'postcss';
|
|
2
|
+
import autoprefixer from 'autoprefixer';
|
|
3
|
+
import postcssPresetEnv from 'postcss-preset-env';
|
|
4
|
+
import cssnano from 'cssnano';
|
|
5
|
+
import { TransformFn } from '../pipeline.js';
|
|
6
|
+
import { logger } from '../../utilities/logger.js';
|
|
7
|
+
|
|
8
|
+
export interface PostcssTransformOptions {
|
|
9
|
+
plugins?: AcceptedPlugin[];
|
|
10
|
+
minify?: boolean;
|
|
11
|
+
[key: string]: any;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function transformPostcss(options: PostcssTransformOptions = {}): TransformFn {
|
|
15
|
+
const plugins: AcceptedPlugin[] = [
|
|
16
|
+
...(options.plugins || []),
|
|
17
|
+
autoprefixer(),
|
|
18
|
+
postcssPresetEnv()
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
if (options.minify) {
|
|
22
|
+
plugins.push(cssnano({ preset: 'default' }));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const processor = postcss(plugins);
|
|
26
|
+
|
|
27
|
+
return async (file) => {
|
|
28
|
+
if (!/\.css$/i.test(file.extname)) return file;
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
const result = await processor.process(file.toString(), {
|
|
32
|
+
from: file.path,
|
|
33
|
+
to: file.path
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
file.contents = Buffer.from(result.css);
|
|
37
|
+
return file;
|
|
38
|
+
} catch (err: any) {
|
|
39
|
+
logger.error('postcss', `Error processing ${file.path}: ${err.message}`);
|
|
40
|
+
throw err;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import path from 'node:path';
|
|
2
|
+
import { TransformFn } from '../pipeline.js';
|
|
3
|
+
import { RenameOptions } from '../../types/index.js';
|
|
4
|
+
|
|
5
|
+
export function transformRename(
|
|
6
|
+
options: string | Function | RenameOptions = {}
|
|
7
|
+
): TransformFn {
|
|
8
|
+
return async (file) => {
|
|
9
|
+
if (typeof options === 'string') {
|
|
10
|
+
file.path = path.resolve(path.dirname(file.path), options);
|
|
11
|
+
return file;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
if (typeof options === 'function') {
|
|
15
|
+
const parsed = {
|
|
16
|
+
dirname: path.dirname(file.path),
|
|
17
|
+
basename: file.stem,
|
|
18
|
+
extname: file.extname
|
|
19
|
+
};
|
|
20
|
+
const result = options(parsed);
|
|
21
|
+
if (result) {
|
|
22
|
+
if (result.dirname) file.path = path.join(result.dirname, path.basename(file.path));
|
|
23
|
+
if (result.basename) file.stem = result.basename;
|
|
24
|
+
if (result.extname) file.extname = result.extname;
|
|
25
|
+
}
|
|
26
|
+
return file;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const { dirname, prefix = '', basename, suffix = '', extname } = options;
|
|
30
|
+
const currentDir = dirname ? path.resolve(file.cwd, dirname) : path.dirname(file.path);
|
|
31
|
+
const base = basename !== undefined ? basename : file.stem;
|
|
32
|
+
const ext = extname !== undefined ? extname : file.extname;
|
|
33
|
+
|
|
34
|
+
file.path = path.join(currentDir, `${prefix}${base}${suffix}${ext.startsWith('.') ? ext : `.${ext}`}`);
|
|
35
|
+
return file;
|
|
36
|
+
};
|
|
37
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { TransformFn } from '../pipeline.js';
|
|
2
|
+
import { ReplaceOptions } from '../../types/index.js';
|
|
3
|
+
|
|
4
|
+
export function transformReplace(
|
|
5
|
+
options: ReplaceOptions | ReplaceOptions[] | string | RegExp,
|
|
6
|
+
replacement?: string | ((substring: string, ...args: any[]) => string)
|
|
7
|
+
): TransformFn {
|
|
8
|
+
let rules: ReplaceOptions[] = [];
|
|
9
|
+
|
|
10
|
+
if (Array.isArray(options)) {
|
|
11
|
+
rules = options;
|
|
12
|
+
} else if (typeof options === 'object' && 'substr' in options) {
|
|
13
|
+
rules = [options];
|
|
14
|
+
} else if (typeof options === 'string' || options instanceof RegExp) {
|
|
15
|
+
rules = [{ substr: options, replacement: replacement || '' }];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return async (file) => {
|
|
19
|
+
let content = file.toString();
|
|
20
|
+
for (const rule of rules) {
|
|
21
|
+
if (typeof rule.replacement === 'function') {
|
|
22
|
+
content = content.replace(rule.substr, rule.replacement as any);
|
|
23
|
+
} else {
|
|
24
|
+
content = content.replace(rule.substr, rule.replacement);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
file.contents = Buffer.from(content);
|
|
28
|
+
return file;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import * as sass from 'sass';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import { TransformFn } from '../pipeline.js';
|
|
5
|
+
import { logger } from '../../utilities/logger.js';
|
|
6
|
+
|
|
7
|
+
export interface SassTransformOptions {
|
|
8
|
+
includePaths?: string[];
|
|
9
|
+
alias?: Record<string, string>;
|
|
10
|
+
style?: 'expanded' | 'compressed';
|
|
11
|
+
sourceMap?: boolean;
|
|
12
|
+
[key: string]: any;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function resolveFile(baseDir: string, url: string): string | null {
|
|
16
|
+
const possiblePaths = [
|
|
17
|
+
path.resolve(baseDir, url),
|
|
18
|
+
path.resolve(baseDir, `${url}.scss`),
|
|
19
|
+
path.resolve(baseDir, `${url}.sass`),
|
|
20
|
+
path.resolve(baseDir, `${url}.css`),
|
|
21
|
+
path.resolve(baseDir, path.dirname(url), `_${path.basename(url)}.scss`),
|
|
22
|
+
path.resolve(baseDir, path.dirname(url), `_${path.basename(url)}.sass`),
|
|
23
|
+
path.resolve(baseDir, url, '_index.scss'),
|
|
24
|
+
path.resolve(baseDir, url, '_index.sass'),
|
|
25
|
+
path.resolve(baseDir, url, 'index.scss'),
|
|
26
|
+
path.resolve(baseDir, url, 'index.sass')
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
for (const p of possiblePaths) {
|
|
30
|
+
try {
|
|
31
|
+
if (fs.existsSync(p) && fs.statSync(p).isFile()) {
|
|
32
|
+
return p;
|
|
33
|
+
}
|
|
34
|
+
} catch {
|
|
35
|
+
// ignore
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function transformSass(options: SassTransformOptions = {}): TransformFn {
|
|
42
|
+
return async (file) => {
|
|
43
|
+
// Only process .scss / .sass files
|
|
44
|
+
if (!/\.(scss|sass)$/i.test(file.extname)) return file;
|
|
45
|
+
// Skip partials starting with '_'
|
|
46
|
+
if (path.basename(file.path).startsWith('_')) return null;
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
const searchDirs = [
|
|
50
|
+
path.dirname(file.path),
|
|
51
|
+
path.resolve(file.cwd, 'node_modules'),
|
|
52
|
+
path.resolve(process.cwd(), 'node_modules'),
|
|
53
|
+
path.resolve(process.cwd(), '..', 'node_modules'),
|
|
54
|
+
...(options.includePaths || [])
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
const customImporter: sass.FileImporter<'async'> = {
|
|
58
|
+
findFileUrl(url) {
|
|
59
|
+
let cleanUrl = url;
|
|
60
|
+
if (cleanUrl.startsWith('~')) {
|
|
61
|
+
cleanUrl = cleanUrl.slice(1);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// 1. Check alias resolution
|
|
65
|
+
if (options.alias) {
|
|
66
|
+
for (const [aliasKey, aliasTarget] of Object.entries(options.alias)) {
|
|
67
|
+
if (cleanUrl === aliasKey) {
|
|
68
|
+
const resolved = resolveFile(path.dirname(aliasTarget), path.basename(aliasTarget));
|
|
69
|
+
if (resolved) return new URL(`file://${resolved}`);
|
|
70
|
+
} else if (cleanUrl.startsWith(`${aliasKey}/`)) {
|
|
71
|
+
const subPath = cleanUrl.slice(aliasKey.length + 1);
|
|
72
|
+
const resolved = resolveFile(aliasTarget, subPath);
|
|
73
|
+
if (resolved) return new URL(`file://${resolved}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// 2. Check searchDirs
|
|
79
|
+
for (const dir of searchDirs) {
|
|
80
|
+
const resolved = resolveFile(dir, cleanUrl);
|
|
81
|
+
if (resolved) {
|
|
82
|
+
return new URL(`file://${resolved}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const result = await sass.compileStringAsync(file.toString(), {
|
|
90
|
+
url: new URL(`file://${file.path}`),
|
|
91
|
+
syntax: file.extname === '.sass' ? 'indented' : 'scss',
|
|
92
|
+
loadPaths: searchDirs,
|
|
93
|
+
importers: [customImporter, new sass.NodePackageImporter()],
|
|
94
|
+
style: options.style || 'expanded',
|
|
95
|
+
sourceMap: options.sourceMap || false
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
file.contents = Buffer.from(result.css);
|
|
99
|
+
file.extname = '.css';
|
|
100
|
+
return file;
|
|
101
|
+
} catch (err: any) {
|
|
102
|
+
logger.error('sass', `Error compiling ${file.path}: ${err.message}`);
|
|
103
|
+
throw err;
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { minify, MinifyOptions } from 'terser';
|
|
2
|
+
import { TransformFn } from '../pipeline.js';
|
|
3
|
+
import { logger } from '../../utilities/logger.js';
|
|
4
|
+
|
|
5
|
+
export function transformTerser(options: MinifyOptions = {}): TransformFn {
|
|
6
|
+
return async (file) => {
|
|
7
|
+
if (!/\.js$/i.test(file.extname)) return file;
|
|
8
|
+
|
|
9
|
+
try {
|
|
10
|
+
const result = await minify(file.toString(), options);
|
|
11
|
+
if (result.code) {
|
|
12
|
+
file.contents = Buffer.from(result.code);
|
|
13
|
+
}
|
|
14
|
+
return file;
|
|
15
|
+
} catch (err: any) {
|
|
16
|
+
logger.error('terser', `Error minifying ${file.path}: ${err.message}`);
|
|
17
|
+
throw err;
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
}
|
|
@@ -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,72 @@
|
|
|
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 cacheOptions = {
|
|
42
|
+
...config.assets.options,
|
|
43
|
+
dontRenameFile: [
|
|
44
|
+
'.html',
|
|
45
|
+
'favicon.ico',
|
|
46
|
+
'manifest.json',
|
|
47
|
+
'robots.txt',
|
|
48
|
+
'*.ico',
|
|
49
|
+
'icons/*',
|
|
50
|
+
'**/icons/**',
|
|
51
|
+
...(config.assets.excludes || []),
|
|
52
|
+
...(config.assets.options?.dontRenameFile || [])
|
|
53
|
+
]
|
|
54
|
+
};
|
|
55
|
+
const revisioner = new AssetRevisioner(cacheOptions);
|
|
56
|
+
const resultFiles = revisioner.process(virtualFiles);
|
|
57
|
+
|
|
58
|
+
// Remove old files that have been renamed
|
|
59
|
+
for (const vFile of resultFiles) {
|
|
60
|
+
if (vFile.revPathOriginal && vFile.revPathOriginal !== vFile.path) {
|
|
61
|
+
await fsUtil.remove(vFile.revPathOriginal);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Write new / updated files
|
|
66
|
+
for (const vFile of resultFiles) {
|
|
67
|
+
await fsUtil.writeFile(vFile.path, vFile.contents);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
logger.success('cache', 'Asset revisioning and rev-manifest.json completed');
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -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
|
+
}
|
package/src/tasks/ftp.ts
ADDED
|
@@ -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
|
+
}
|