@changke/staticnext-build 0.13.0 → 0.26.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 +13 -0
- package/build.mjs +40 -13
- package/lib/tasks/clean.mjs +9 -4
- package/lib/tasks/copy.mjs +54 -5
- package/lib/tasks/markdown.mjs +44 -6
- package/lib/tasks/prototype.mjs +30 -6
- package/lib/tasks/scripts.mjs +3 -3
- package/lib/tasks/styles.mjs +4 -5
- package/lib/typedefs.mjs +9 -1
- package/lib/utils.mjs +96 -3
- package/package.json +12 -15
- package/eslint.config.mjs +0 -30
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,19 @@ All notable changes to this project will be documented in this file.
|
|
|
4
4
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
5
5
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
6
|
|
|
7
|
+
## 0.26.0
|
|
8
|
+
2025-12-21
|
|
9
|
+
### Removed
|
|
10
|
+
- `del`, `cpy` and `globby` to prefer node's native features (`rm`, `cp` and `glob`)
|
|
11
|
+
- `mocha` to prefer node's built-in test runner
|
|
12
|
+
### Added
|
|
13
|
+
- New experimental templating system `JSTmpl` as default
|
|
14
|
+
- Parameter `--njk` to specify Nunjucks as template engine
|
|
15
|
+
- Parameter `--debug` to show verbose information for "copy" and "markdown" tasks
|
|
16
|
+
### Changed
|
|
17
|
+
- Console outputs now with colors
|
|
18
|
+
- Updated various dependencies
|
|
19
|
+
|
|
7
20
|
## 0.13.0
|
|
8
21
|
2025-09-20
|
|
9
22
|
### Changed
|
package/build.mjs
CHANGED
|
@@ -1,19 +1,27 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
2
|
import {argv} from 'node:process';
|
|
3
|
+
import path from 'node:path';
|
|
4
4
|
|
|
5
|
+
import './lib/typedefs.mjs';
|
|
5
6
|
import Config from './lib/config.mjs';
|
|
6
7
|
|
|
7
8
|
import clean from './lib/tasks/clean.mjs';
|
|
8
9
|
import copy from './lib/tasks/copy.mjs';
|
|
9
|
-
import markdown from './lib/tasks/markdown.mjs';
|
|
10
|
-
import prototype from './lib/tasks/prototype.mjs';
|
|
10
|
+
import {markdown, markdown2} from './lib/tasks/markdown.mjs';
|
|
11
|
+
import {prototype, prototype2} from './lib/tasks/prototype.mjs';
|
|
11
12
|
import scripts from './lib/tasks/scripts.mjs';
|
|
12
13
|
import setEnvDev from './lib/tasks/set-env-dev.mjs';
|
|
13
14
|
import setEnvProd from './lib/tasks/set-env-prod.mjs';
|
|
14
15
|
import styles from './lib/tasks/styles.mjs';
|
|
16
|
+
import {consoleLogColored} from './lib/utils.mjs';
|
|
15
17
|
|
|
16
18
|
const taskName = argv[2]?.toLowerCase() || 'dev';
|
|
19
|
+
const useNjk = argv.includes('--njk');
|
|
20
|
+
const debug = argv.includes('--debug');
|
|
21
|
+
|
|
22
|
+
if (debug) {
|
|
23
|
+
process.env.SN_BUILD_MODE = 'debug';
|
|
24
|
+
}
|
|
17
25
|
|
|
18
26
|
// load config data
|
|
19
27
|
const conf = await Config.loadConfig();
|
|
@@ -22,17 +30,23 @@ const taskEnded = taskName => {
|
|
|
22
30
|
console.timeEnd(taskName);
|
|
23
31
|
};
|
|
24
32
|
|
|
25
|
-
const srcPaths = Config.getSourcePaths(conf.sourcePath, conf.sourceRoot);
|
|
26
|
-
const tgtPaths = Config.getTargetPaths(conf.targetPath, conf.targetRoot, conf.targetAssetsRoot);
|
|
33
|
+
const srcPaths = Config.getSourcePaths(conf.sourcePath, path.normalize(conf.sourceRoot));
|
|
34
|
+
const tgtPaths = Config.getTargetPaths(conf.targetPath, path.normalize(conf.targetRoot), conf.targetAssetsRoot);
|
|
27
35
|
|
|
28
36
|
// Set parameters of each task
|
|
29
37
|
const tasks = {
|
|
30
|
-
clean: () => clean([
|
|
38
|
+
clean: () => clean([conf.targetRoot]),
|
|
31
39
|
copy: () => copy([
|
|
32
|
-
|
|
33
|
-
{sources: `${
|
|
34
|
-
|
|
35
|
-
{sources: `${srcPaths.
|
|
40
|
+
// Root2root: src/main/webapp/index.html -> tgt/static/index.html
|
|
41
|
+
{sources: [`${conf.sourceRoot}/*.*`, `!${conf.sourceRoot}/*.d.ts`], target: conf.targetRoot, opts: {flat: true}},
|
|
42
|
+
// Assets: src/main/webapp/assets/dummy.txt -> tgt/static/assets/_sn_/dummy.txt
|
|
43
|
+
{sources: `${srcPaths.assets}/**/*.*`, target: tgtPaths.assets, opts: {levelRemoval: 4}},
|
|
44
|
+
// Module assets:
|
|
45
|
+
// src/main/webapp/modules/mod-weather/assets/weather/sample.json -> tgt/static/assets/_sn_/modules/mod-weather/assets/weather/sample.json
|
|
46
|
+
{sources: `${srcPaths.modules}/**/assets/**/*.*`, target: tgtPaths.moduleAssets, opts: {levelRemoval: 4}},
|
|
47
|
+
// Markdown file assets
|
|
48
|
+
// src/main/webapp/md/google-pixel-9/assets/cover.webp -> public/posts/google-pixel-9/assets/cover.webp
|
|
49
|
+
{sources: `${srcPaths.markdown}/**/assets/**/*.*`, target: tgtPaths.markdown, opts: {levelRemoval: 4}}
|
|
36
50
|
].concat(Config.getCopyPairs(conf.copyPairs, conf.sourceRoot, tgtPaths.assets))),
|
|
37
51
|
markdown: () => markdown(
|
|
38
52
|
srcPaths.markdown,
|
|
@@ -40,12 +54,22 @@ const tasks = {
|
|
|
40
54
|
conf.njkGlobals,
|
|
41
55
|
tgtPaths.markdown
|
|
42
56
|
),
|
|
57
|
+
markdown2: () => markdown2(
|
|
58
|
+
srcPaths.markdown,
|
|
59
|
+
conf.njkGlobals,
|
|
60
|
+
tgtPaths.markdown
|
|
61
|
+
),
|
|
43
62
|
prototype: () => prototype(
|
|
44
63
|
srcPaths.prototype,
|
|
45
64
|
[srcPaths.modules],
|
|
46
65
|
conf.njkGlobals,
|
|
47
66
|
tgtPaths.prototype
|
|
48
67
|
),
|
|
68
|
+
prototype2: () => prototype2(
|
|
69
|
+
srcPaths.prototype,
|
|
70
|
+
conf.njkGlobals,
|
|
71
|
+
tgtPaths.prototype
|
|
72
|
+
),
|
|
49
73
|
scripts: () => scripts(Config.getScriptEntries(conf.moduleEntries, conf.mainEntryFile, srcPaths), tgtPaths.assets),
|
|
50
74
|
styles: () => styles([
|
|
51
75
|
`${srcPaths.css}/brands/*.css`,
|
|
@@ -57,7 +81,7 @@ const tasks = {
|
|
|
57
81
|
|
|
58
82
|
const paraDev = () => {
|
|
59
83
|
return Promise.all([
|
|
60
|
-
tasks.markdown().then(tasks.prototype),
|
|
84
|
+
useNjk ? tasks.markdown().then(tasks.prototype) : tasks.markdown2().then(tasks.prototype2),
|
|
61
85
|
tasks.styles(),
|
|
62
86
|
tasks.scripts()
|
|
63
87
|
]);
|
|
@@ -65,7 +89,7 @@ const paraDev = () => {
|
|
|
65
89
|
|
|
66
90
|
const paraProd = () => {
|
|
67
91
|
return Promise.all([
|
|
68
|
-
tasks.markdown(),
|
|
92
|
+
useNjk ? tasks.markdown() : tasks.markdown2(),
|
|
69
93
|
tasks.styles(),
|
|
70
94
|
tasks.scripts()
|
|
71
95
|
]);
|
|
@@ -83,7 +107,9 @@ const taskMap = {
|
|
|
83
107
|
'clean': tasks.clean,
|
|
84
108
|
'copy': tasks.copy,
|
|
85
109
|
'markdown': tasks.markdown,
|
|
110
|
+
'markdown2': tasks.markdown2,
|
|
86
111
|
'prototype': tasks.prototype,
|
|
112
|
+
'prototype2': tasks.prototype2,
|
|
87
113
|
'scripts': tasks.scripts,
|
|
88
114
|
'setenvdev': setEnvDev,
|
|
89
115
|
'setenvprod': setEnvProd,
|
|
@@ -94,10 +120,11 @@ const taskMap = {
|
|
|
94
120
|
|
|
95
121
|
const task = taskMap[taskName];
|
|
96
122
|
|
|
97
|
-
|
|
123
|
+
consoleLogColored('+++ SN Build +++', ['bold', 'blue']);
|
|
98
124
|
console.time(taskName);
|
|
99
125
|
console.log(`Task "${taskName}" started...`);
|
|
100
126
|
task().then(() => {
|
|
127
|
+
console.log('Task ended.');
|
|
101
128
|
taskEnded(taskName);
|
|
102
129
|
}).catch(err => {
|
|
103
130
|
console.error(err);
|
package/lib/tasks/clean.mjs
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {rm} from 'node:fs/promises';
|
|
2
|
+
import {consoleLogColored} from '../utils.mjs';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Clean the target directory
|
|
5
6
|
*
|
|
7
|
+
* @example
|
|
8
|
+
* await clean('target');
|
|
9
|
+
*
|
|
6
10
|
* @param {(string|string[])} targetPath
|
|
7
|
-
* @returns {Promise<
|
|
11
|
+
* @returns {Promise<Awaited<void>[]>}
|
|
8
12
|
*/
|
|
9
13
|
const clean = targetPath => {
|
|
10
|
-
|
|
11
|
-
|
|
14
|
+
consoleLogColored('=> clean');
|
|
15
|
+
let pa = (Array.isArray(targetPath)) ? targetPath : [targetPath];
|
|
16
|
+
return Promise.all(pa.map(path => rm(path, {force: true, recursive: true})));
|
|
12
17
|
};
|
|
13
18
|
|
|
14
19
|
export {clean};
|
package/lib/tasks/copy.mjs
CHANGED
|
@@ -1,15 +1,64 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {cp} from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import {glob2Array, consoleLogColored, isDebugMode} from '../utils.mjs';
|
|
2
4
|
|
|
3
5
|
/**
|
|
4
6
|
* Copy (different) assets
|
|
5
7
|
*
|
|
6
8
|
* @param {STPair[]} sourceTargetPairs
|
|
7
|
-
* @return {Promise<Awaited<
|
|
9
|
+
* @return {Promise<Awaited<Awaited<void>[]>[]>}
|
|
8
10
|
*/
|
|
9
11
|
const copy = sourceTargetPairs => {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
+
consoleLogColored('=> copy');
|
|
13
|
+
const debug = isDebugMode();
|
|
14
|
+
return Promise.all(sourceTargetPairs.map(pair => globCopy(pair.sources, pair.target, pair.opts, debug)));
|
|
12
15
|
};
|
|
13
16
|
|
|
14
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Copy files specified as glob pattern(s)
|
|
19
|
+
*
|
|
20
|
+
* @param {string|string[]} globPatterns
|
|
21
|
+
* @param {string} targetRoot
|
|
22
|
+
* @param {CopyTaskOption} opts
|
|
23
|
+
* @param {boolean} verbose
|
|
24
|
+
* @returns {Promise<Awaited<void>[]>}
|
|
25
|
+
*/
|
|
26
|
+
const globCopy = async (globPatterns, targetRoot, opts, verbose = false) => {
|
|
27
|
+
/** @type string[] */
|
|
28
|
+
const sources = await glob2Array(globPatterns);
|
|
29
|
+
|
|
30
|
+
let targetPath = '';
|
|
31
|
+
return Promise.all(sources.map(async src => {
|
|
32
|
+
if (opts.flat) {
|
|
33
|
+
targetPath = path.join(targetRoot, path.basename(src));
|
|
34
|
+
} else {
|
|
35
|
+
targetPath = path.join(targetRoot, genTargetPath(src, opts.levelRemoval));
|
|
36
|
+
}
|
|
37
|
+
if (verbose) {
|
|
38
|
+
consoleLogColored(`copying ${src} -> ${targetPath}`, 'dim');
|
|
39
|
+
}
|
|
40
|
+
return cp(src, targetPath, {recursive: true});
|
|
41
|
+
}));
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Generate the target path (destination) of the file to be copied
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* genTargetPath('src/foo/bar/a.txt', 2)
|
|
49
|
+
* // returns 'bar/a.txt', 2 levels of path (src/foo) are removed
|
|
50
|
+
*
|
|
51
|
+
* @param {string} sourcePath
|
|
52
|
+
* @param {number=} levelRemoval How many levels of path to be removed
|
|
53
|
+
* @returns {string}
|
|
54
|
+
*/
|
|
55
|
+
const genTargetPath = (sourcePath, levelRemoval = 0) => {
|
|
56
|
+
const pathParts = sourcePath.split(path.sep);
|
|
57
|
+
const pathPartsCount = pathParts.length - 1;
|
|
58
|
+
const removal = levelRemoval ? Math.min(levelRemoval, pathPartsCount) : pathPartsCount;
|
|
59
|
+
const newPathParts = pathParts.slice(removal);
|
|
60
|
+
return newPathParts.join(path.sep);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export {copy, genTargetPath};
|
|
15
64
|
export default copy;
|
package/lib/tasks/markdown.mjs
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import {globby} from 'globby';
|
|
2
1
|
import {readFile} from 'node:fs/promises';
|
|
3
2
|
import {marked} from 'marked';
|
|
4
3
|
import {markedHighlight} from 'marked-highlight';
|
|
5
4
|
import {markedXhtml} from 'marked-xhtml';
|
|
6
5
|
import hljs from 'highlight.js';
|
|
7
6
|
import njk from 'nunjucks';
|
|
8
|
-
|
|
9
|
-
import {createAndWriteToFile, getTargetPathString} from '../utils.mjs';
|
|
7
|
+
import {renderPageToString} from '@changke/jstmpl';
|
|
8
|
+
import {createAndWriteToFile, getTargetPathString, glob2Array, consoleLogColored, isDebugMode} from '../utils.mjs';
|
|
10
9
|
|
|
11
10
|
marked.use({
|
|
12
11
|
headerIds: false,
|
|
@@ -39,6 +38,8 @@ const getPageTitle = content => {
|
|
|
39
38
|
return t;
|
|
40
39
|
};
|
|
41
40
|
|
|
41
|
+
const mdArticleTemplateFile = 'md.tmpl.mjs'; // TODO: configurable?
|
|
42
|
+
|
|
42
43
|
/**
|
|
43
44
|
* Generate docs using markdown and Nunjucks
|
|
44
45
|
*
|
|
@@ -49,8 +50,8 @@ const getPageTitle = content => {
|
|
|
49
50
|
* @return {Promise<string[]>}
|
|
50
51
|
*/
|
|
51
52
|
const markdown = (mdSourcePathRoot, njkPaths, njkGlobals, mdTargetPath) => {
|
|
52
|
-
|
|
53
|
-
return
|
|
53
|
+
consoleLogColored('=> markdown (njk)');
|
|
54
|
+
return glob2Array(`${mdSourcePathRoot}/**/*.md`).then(docs => {
|
|
54
55
|
const njkEnv = new Environment(new FileSystemLoader(njkPaths, {}));
|
|
55
56
|
for (const [k, v] of Object.entries(njkGlobals)) {
|
|
56
57
|
njkEnv.addGlobal(k, v);
|
|
@@ -85,5 +86,42 @@ const markdown = (mdSourcePathRoot, njkPaths, njkGlobals, mdTargetPath) => {
|
|
|
85
86
|
});
|
|
86
87
|
};
|
|
87
88
|
|
|
88
|
-
|
|
89
|
+
const markdown2 = (mdSourcePathRoot, data, mdTargetPath) => {
|
|
90
|
+
const debug = isDebugMode();
|
|
91
|
+
consoleLogColored('=> markdown2 (jstmpl)');
|
|
92
|
+
return glob2Array(`${mdSourcePathRoot}/**/*.md`).then(docs => {
|
|
93
|
+
return Promise.all(docs.map(async doc => {
|
|
94
|
+
const mdContent = await readFile(doc, {encoding: 'utf8'});
|
|
95
|
+
const mdHtml = marked.parse(mdContent);
|
|
96
|
+
const pageTitle = getPageTitle(mdContent);
|
|
97
|
+
const res = await renderPageToString(`${mdSourcePathRoot}/${mdArticleTemplateFile}`, {
|
|
98
|
+
...data,
|
|
99
|
+
title: pageTitle,
|
|
100
|
+
content: mdHtml
|
|
101
|
+
});
|
|
102
|
+
const target = getTargetPathString(
|
|
103
|
+
doc,
|
|
104
|
+
mdSourcePathRoot,
|
|
105
|
+
mdTargetPath,
|
|
106
|
+
'md',
|
|
107
|
+
'html'
|
|
108
|
+
);
|
|
109
|
+
if (debug) {
|
|
110
|
+
consoleLogColored(`md: ${doc} -> ${target}`, 'dim');
|
|
111
|
+
}
|
|
112
|
+
await createAndWriteToFile(target, res);
|
|
113
|
+
// create a content-only file for RSS generating
|
|
114
|
+
const feedTarget = getTargetPathString(
|
|
115
|
+
doc,
|
|
116
|
+
mdSourcePathRoot,
|
|
117
|
+
mdTargetPath,
|
|
118
|
+
'md',
|
|
119
|
+
'txt'
|
|
120
|
+
);
|
|
121
|
+
await createAndWriteToFile(feedTarget, mdHtml);
|
|
122
|
+
}));
|
|
123
|
+
});
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
export {markdown, markdown2};
|
|
89
127
|
export default markdown;
|
package/lib/tasks/prototype.mjs
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import njk from 'nunjucks';
|
|
2
|
-
import {globby} from 'globby';
|
|
3
2
|
import {readFile} from 'node:fs/promises';
|
|
4
|
-
|
|
5
|
-
import {getTargetPathString, createAndWriteToFile} from '../utils.mjs';
|
|
3
|
+
import {renderPageToString} from '@changke/jstmpl';
|
|
4
|
+
import {getTargetPathString, createAndWriteToFile, glob2Array, consoleLogColored} from '../utils.mjs';
|
|
6
5
|
|
|
7
6
|
const {Environment, FileSystemLoader} = njk;
|
|
8
7
|
|
|
@@ -16,8 +15,8 @@ const {Environment, FileSystemLoader} = njk;
|
|
|
16
15
|
* @return {Promise<string[]>}
|
|
17
16
|
*/
|
|
18
17
|
const prototype = (prototypeSourcePathRoot, njkPaths, njkGlobals, prototypeTargetPath) => {
|
|
19
|
-
|
|
20
|
-
return
|
|
18
|
+
consoleLogColored('=> prototype (njk)');
|
|
19
|
+
return glob2Array(`${prototypeSourcePathRoot}/pages/**/*.njk`).then(pages => {
|
|
21
20
|
const njkEnv = new Environment(new FileSystemLoader([prototypeSourcePathRoot].concat(njkPaths), {}));
|
|
22
21
|
for (const [k, v] of Object.entries(njkGlobals)) {
|
|
23
22
|
njkEnv.addGlobal(k, v);
|
|
@@ -37,5 +36,30 @@ const prototype = (prototypeSourcePathRoot, njkPaths, njkGlobals, prototypeTarge
|
|
|
37
36
|
});
|
|
38
37
|
};
|
|
39
38
|
|
|
40
|
-
|
|
39
|
+
/**
|
|
40
|
+
* Generate static pages with JSTmpl
|
|
41
|
+
* @param {string} sourceRoot - Glob pattern for source files e.g. 'src/pages/*.mjs'
|
|
42
|
+
* @param {Object.<string, string>} data
|
|
43
|
+
* @param {string} targetPath
|
|
44
|
+
* @returns {Promise<void>}
|
|
45
|
+
*/
|
|
46
|
+
const prototype2 = (sourceRoot, data, targetPath) => {
|
|
47
|
+
consoleLogColored('=> prototype (jstmpl)');
|
|
48
|
+
// src/main/webapp/prototype/pages/index.mjs -> ${targetPath.prototype}/index.html
|
|
49
|
+
return glob2Array(`${sourceRoot}/pages/**/*.mjs`).then(pages => {
|
|
50
|
+
return Promise.all(pages.map(async page => {
|
|
51
|
+
const res = await renderPageToString(page, data);
|
|
52
|
+
const targetFile = getTargetPathString(
|
|
53
|
+
page,
|
|
54
|
+
`${sourceRoot}/pages`,
|
|
55
|
+
targetPath,
|
|
56
|
+
'mjs',
|
|
57
|
+
page.endsWith('rss/index.mjs') ? 'xml' : 'html'
|
|
58
|
+
);
|
|
59
|
+
await createAndWriteToFile(targetFile, res);
|
|
60
|
+
}));
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export {prototype, prototype2};
|
|
41
65
|
export default prototype;
|
package/lib/tasks/scripts.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {build} from 'esbuild';
|
|
2
|
-
|
|
3
2
|
import isEnvProd from '../is-prod.mjs';
|
|
3
|
+
import {consoleLogColored} from '../utils.mjs';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Transpile / bundle JS files using esbuild
|
|
@@ -10,12 +10,12 @@ import isEnvProd from '../is-prod.mjs';
|
|
|
10
10
|
* @return {Promise}
|
|
11
11
|
*/
|
|
12
12
|
const esm = (entries, targetPath) => {
|
|
13
|
-
|
|
13
|
+
consoleLogColored('=> scripts');
|
|
14
14
|
return build({
|
|
15
15
|
entryPoints: entries,
|
|
16
16
|
bundle: true,
|
|
17
17
|
format: 'esm',
|
|
18
|
-
target: '
|
|
18
|
+
target: 'es2024',
|
|
19
19
|
splitting: true,
|
|
20
20
|
outdir: targetPath,
|
|
21
21
|
minify: isEnvProd(), // only minify in prod build
|
package/lib/tasks/styles.mjs
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import {build} from 'esbuild';
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import {glob2Array, consoleLogColored} from '../utils.mjs';
|
|
4
3
|
import isEnvProd from '../is-prod.mjs';
|
|
5
4
|
|
|
6
5
|
/**
|
|
@@ -12,12 +11,12 @@ import isEnvProd from '../is-prod.mjs';
|
|
|
12
11
|
* @return {Promise}
|
|
13
12
|
*/
|
|
14
13
|
const styles = (sources, targetPath, external = []) => {
|
|
15
|
-
|
|
16
|
-
return
|
|
14
|
+
consoleLogColored('=> styles');
|
|
15
|
+
return glob2Array(sources).then(entries => {
|
|
17
16
|
return build({
|
|
18
17
|
entryPoints: entries,
|
|
19
18
|
bundle: true,
|
|
20
|
-
target: '
|
|
19
|
+
target: 'es2024',
|
|
21
20
|
outdir: targetPath,
|
|
22
21
|
sourcemap: !isEnvProd(),
|
|
23
22
|
minify: isEnvProd(),
|
package/lib/typedefs.mjs
CHANGED
|
@@ -14,13 +14,21 @@
|
|
|
14
14
|
* @property {string=} markdown Path for markdown files
|
|
15
15
|
*/
|
|
16
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Options for "copy" task
|
|
19
|
+
*
|
|
20
|
+
* @typedef {Object} CopyTaskOption
|
|
21
|
+
* @property {number=} levelRemoval
|
|
22
|
+
* @property {boolean=} flat
|
|
23
|
+
*/
|
|
24
|
+
|
|
17
25
|
/**
|
|
18
26
|
* Source/Target pairs for "copy" task
|
|
19
27
|
*
|
|
20
28
|
* @typedef {Object} STPair
|
|
21
29
|
* @property {(string|string[])} sources
|
|
22
30
|
* @property {string} target
|
|
23
|
-
* @property {
|
|
31
|
+
* @property {CopyTaskOption=} opts
|
|
24
32
|
*/
|
|
25
33
|
|
|
26
34
|
/**
|
package/lib/utils.mjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import {access, mkdir, writeFile} from 'node:fs/promises';
|
|
2
|
-
import
|
|
1
|
+
import {access, mkdir, writeFile, glob, rm, stat} from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import {styleText} from 'node:util';
|
|
4
|
+
import {env} from 'node:process';
|
|
3
5
|
|
|
4
6
|
const getTargetPathString = (sourcePath, sourceRoot, targetRoot, srcExt, tgtExt) => {
|
|
5
7
|
const baseName = path.basename(sourcePath, `.${srcExt}`);
|
|
@@ -17,7 +19,98 @@ const createAndWriteToFile = async (filePath, fileContent) => {
|
|
|
17
19
|
await writeFile(filePath, fileContent, {flag: 'w+'});
|
|
18
20
|
};
|
|
19
21
|
|
|
22
|
+
/**
|
|
23
|
+
* Extracts negative patterns into another array to feed Node's glob function
|
|
24
|
+
* @param {(string|string[])} globPatterns
|
|
25
|
+
* @returns {Object.<string, string[]>}
|
|
26
|
+
*/
|
|
27
|
+
const processGlobPatterns = globPatterns => {
|
|
28
|
+
// extract negative patterns
|
|
29
|
+
let patterns = globPatterns;
|
|
30
|
+
if (!Array.isArray(globPatterns)) {
|
|
31
|
+
patterns = [globPatterns];
|
|
32
|
+
}
|
|
33
|
+
/** @type string[] */
|
|
34
|
+
const negativePatterns = [];
|
|
35
|
+
/** @type string[] */
|
|
36
|
+
const normalPatterns = patterns.filter(p => {
|
|
37
|
+
if (p.startsWith('!')) {
|
|
38
|
+
negativePatterns.push(p.substring(1));
|
|
39
|
+
return false;
|
|
40
|
+
} else {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
return {normalPatterns, negativePatterns};
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Convert `glob()` result to an array
|
|
49
|
+
* @param {(string|string[])} globPatterns
|
|
50
|
+
* @param {boolean} fileOnly
|
|
51
|
+
* @returns {Promise<string[]>}
|
|
52
|
+
*/
|
|
53
|
+
const glob2Array = async (globPatterns, fileOnly = false) => {
|
|
54
|
+
const p = processGlobPatterns(globPatterns);
|
|
55
|
+
const m = glob(p.normalPatterns, {exclude: p.negativePatterns});
|
|
56
|
+
if (fileOnly) {
|
|
57
|
+
const files = [];
|
|
58
|
+
return Array.fromAsync(m).then(async entries => {
|
|
59
|
+
for (const entry of entries) {
|
|
60
|
+
const st = await stat(entry);
|
|
61
|
+
if (st.isFile()) {
|
|
62
|
+
files.push(entry);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return files;
|
|
66
|
+
});
|
|
67
|
+
} else {
|
|
68
|
+
return Array.fromAsync(m);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const createDirectory = async (dir, log = false) => {
|
|
73
|
+
try {
|
|
74
|
+
const dirUrl = new URL(dir, import.meta.url);
|
|
75
|
+
const createdDir = await mkdir(dirUrl, {recursive: true});
|
|
76
|
+
if (log) {
|
|
77
|
+
console.log(`Directory created: ${createdDir}`);
|
|
78
|
+
}
|
|
79
|
+
return createdDir;
|
|
80
|
+
} catch (err) {
|
|
81
|
+
console.error(err.message);
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
const removeDirectory = async (dir, log = false) => {
|
|
86
|
+
try {
|
|
87
|
+
await rm(dir, {recursive: true, force: true});
|
|
88
|
+
if (log) {
|
|
89
|
+
console.log(`Directory removed: ${dir}`);
|
|
90
|
+
}
|
|
91
|
+
} catch (err) {
|
|
92
|
+
console.error(err.message);
|
|
93
|
+
}
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Print colored console messages
|
|
98
|
+
* @param {string} msg
|
|
99
|
+
* @param {(string|string[])} color
|
|
100
|
+
*/
|
|
101
|
+
const consoleLogColored = (msg, color = 'cyan') => {
|
|
102
|
+
console.log(styleText(color, msg));
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const isDebugMode = () => (env.SN_BUILD_MODE === 'debug');
|
|
106
|
+
|
|
20
107
|
export {
|
|
21
108
|
getTargetPathString,
|
|
22
|
-
createAndWriteToFile
|
|
109
|
+
createAndWriteToFile,
|
|
110
|
+
processGlobPatterns,
|
|
111
|
+
glob2Array,
|
|
112
|
+
createDirectory,
|
|
113
|
+
removeDirectory,
|
|
114
|
+
consoleLogColored,
|
|
115
|
+
isDebugMode
|
|
23
116
|
};
|
package/package.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@changke/staticnext-build",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.26.1",
|
|
4
4
|
"description": "Build scripts extracted from StaticNext seed project",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"lint": "eslint . lib test",
|
|
8
|
-
"test": "
|
|
8
|
+
"test": "node --test",
|
|
9
9
|
"prepublishOnly": "npm run lint && npm run test"
|
|
10
10
|
},
|
|
11
11
|
"engines": {
|
|
12
|
-
"node": ">=
|
|
12
|
+
"node": ">=22.14.0"
|
|
13
13
|
},
|
|
14
14
|
"bin": {
|
|
15
15
|
"sn-build": "build.mjs"
|
|
@@ -19,21 +19,18 @@
|
|
|
19
19
|
"license": "ISC",
|
|
20
20
|
"type": "module",
|
|
21
21
|
"devDependencies": {
|
|
22
|
-
"@eslint/js": "^9.
|
|
23
|
-
"@stylistic/eslint-plugin": "^5.
|
|
24
|
-
"eslint": "^9.
|
|
25
|
-
"globals": "^16.
|
|
26
|
-
"mocha": "^11.7.2"
|
|
22
|
+
"@eslint/js": "^9.39.2",
|
|
23
|
+
"@stylistic/eslint-plugin": "^5.6.1",
|
|
24
|
+
"eslint": "^9.39.2",
|
|
25
|
+
"globals": "^16.5.0"
|
|
27
26
|
},
|
|
28
27
|
"dependencies": {
|
|
29
|
-
"
|
|
30
|
-
"
|
|
31
|
-
"esbuild": "^0.25.10",
|
|
32
|
-
"globby": "^14.1.0",
|
|
28
|
+
"@changke/jstmpl": "^0.0.1",
|
|
29
|
+
"esbuild": "^0.27.2",
|
|
33
30
|
"highlight.js": "^11.11.1",
|
|
34
|
-
"marked": "^
|
|
35
|
-
"marked-highlight": "^2.2.
|
|
36
|
-
"marked-xhtml": "^1.0.
|
|
31
|
+
"marked": "^17.0.1",
|
|
32
|
+
"marked-highlight": "^2.2.3",
|
|
33
|
+
"marked-xhtml": "^1.0.14",
|
|
37
34
|
"nunjucks": "^3.2.4"
|
|
38
35
|
}
|
|
39
36
|
}
|
package/eslint.config.mjs
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import globals from 'globals';
|
|
2
|
-
import js from '@eslint/js';
|
|
3
|
-
import stylistic from '@stylistic/eslint-plugin';
|
|
4
|
-
|
|
5
|
-
export default [
|
|
6
|
-
js.configs.recommended,
|
|
7
|
-
stylistic.configs.customize({
|
|
8
|
-
blockSpacing: false,
|
|
9
|
-
braceStyle: '1tbs',
|
|
10
|
-
semi: true
|
|
11
|
-
}),
|
|
12
|
-
{
|
|
13
|
-
files: ['**/*.mjs'],
|
|
14
|
-
languageOptions: {
|
|
15
|
-
globals: {
|
|
16
|
-
...globals.node,
|
|
17
|
-
...globals.mocha
|
|
18
|
-
}
|
|
19
|
-
},
|
|
20
|
-
rules: {
|
|
21
|
-
'@stylistic/arrow-parens': ['error', 'as-needed', {requireForBlockBody: false}],
|
|
22
|
-
'@stylistic/comma-dangle': ['error', 'never'],
|
|
23
|
-
'@stylistic/object-curly-spacing': ['error', 'never'],
|
|
24
|
-
'@stylistic/quotes': ['warn', 'single'],
|
|
25
|
-
'@stylistic/quote-props': 0,
|
|
26
|
-
'@stylistic/semi': ['error', 'always'],
|
|
27
|
-
'@stylistic/space-before-function-paren': ['error', {anonymous: 'ignore', asyncArrow: 'always', named: 'never'}]
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
];
|