@ywal123456/jskim 0.1.1 → 0.3.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.
- package/README.md +113 -145
- package/docs/configuration.md +217 -175
- package/docs/create-jskim.md +22 -7
- package/package.json +1 -1
- package/scripts/commands/dev-command.js +10 -109
- package/scripts/commands/watch-command.js +15 -25
- package/scripts/lib/build-project.js +18 -12
- package/scripts/lib/create-nunjucks-env.js +39 -1
- package/scripts/lib/create-watch-runtime.js +516 -0
- package/scripts/lib/merge-config.js +87 -2
- package/scripts/lib/process-files.js +263 -0
- package/scripts/lib/render-pages.js +17 -1
- package/scripts/lib/resolve-project.js +152 -28
- package/scripts/lib/resolve-watch-paths.js +31 -13
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const {
|
|
4
|
-
const { resolveProject } = require('../lib/resolve-project');
|
|
5
|
-
const { createProjectWatcher } = require('../lib/create-project-watcher');
|
|
6
|
-
const { createStaticServer } = require('../lib/create-static-server');
|
|
7
|
-
const { createLiveReload } = require('../lib/create-live-reload');
|
|
3
|
+
const { createWatchRuntime } = require('../lib/create-watch-runtime');
|
|
8
4
|
const { registerShutdown } = require('./register-shutdown');
|
|
9
|
-
const { toDisplayPath } = require('./path-display');
|
|
10
|
-
const { formatListenError } = require('./serve-errors');
|
|
11
5
|
|
|
12
6
|
/**
|
|
13
7
|
* dev コマンドを実行します。
|
|
@@ -22,51 +16,15 @@ async function runDevCommand(options = {}) {
|
|
|
22
16
|
const usageLine =
|
|
23
17
|
options.usageLine || 'npm run dev -- <project-name>';
|
|
24
18
|
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
config,
|
|
19
|
+
const runtime = createWatchRuntime({
|
|
20
|
+
mode: 'dev',
|
|
28
21
|
workspaceRoot,
|
|
29
22
|
projectName: options.projectName,
|
|
30
23
|
commandName: 'dev',
|
|
31
24
|
usageLine,
|
|
32
25
|
});
|
|
33
26
|
|
|
34
|
-
const host = project.serve.host.trim();
|
|
35
|
-
const port = project.serve.port;
|
|
36
|
-
const liveReloadEnabled = project.dev.liveReload;
|
|
37
|
-
const outputDisplay = toDisplayPath(project.outputDir, workspaceRoot);
|
|
38
|
-
|
|
39
|
-
const liveReload = createLiveReload({
|
|
40
|
-
projectName: project.name,
|
|
41
|
-
enabled: liveReloadEnabled,
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
const staticServer = createStaticServer({
|
|
45
|
-
rootDir: project.outputDir,
|
|
46
|
-
host,
|
|
47
|
-
port,
|
|
48
|
-
projectName: project.name,
|
|
49
|
-
handleInternalRequest: (req, res, meta) =>
|
|
50
|
-
liveReload.handleRequest(req, res, meta),
|
|
51
|
-
transformHtml: liveReloadEnabled
|
|
52
|
-
? (html) => liveReload.injectHtml(html)
|
|
53
|
-
: undefined,
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
const projectWatcher = createProjectWatcher(project, {
|
|
57
|
-
runInitialBuild: true,
|
|
58
|
-
logChanges: true,
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
// 成功した再ビルドのあとだけ reload(初回はクライアント未接続のため不要)
|
|
62
|
-
projectWatcher.on('build:success', ({ initial }) => {
|
|
63
|
-
if (!initial && liveReloadEnabled) {
|
|
64
|
-
liveReload.broadcastReload();
|
|
65
|
-
}
|
|
66
|
-
});
|
|
67
|
-
|
|
68
27
|
let stopping = false;
|
|
69
|
-
let watcherStarted = false;
|
|
70
28
|
|
|
71
29
|
async function shutdown() {
|
|
72
30
|
if (stopping) {
|
|
@@ -75,21 +33,7 @@ async function runDevCommand(options = {}) {
|
|
|
75
33
|
stopping = true;
|
|
76
34
|
|
|
77
35
|
try {
|
|
78
|
-
|
|
79
|
-
await projectWatcher.close();
|
|
80
|
-
}
|
|
81
|
-
} catch {
|
|
82
|
-
// 終了時エラーは無視
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
try {
|
|
86
|
-
liveReload.close();
|
|
87
|
-
} catch {
|
|
88
|
-
// 終了時エラーは無視
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
try {
|
|
92
|
-
await staticServer.stop();
|
|
36
|
+
await runtime.close();
|
|
93
37
|
} catch {
|
|
94
38
|
// 終了時エラーは無視
|
|
95
39
|
}
|
|
@@ -98,65 +42,22 @@ async function runDevCommand(options = {}) {
|
|
|
98
42
|
process.exit(0);
|
|
99
43
|
}
|
|
100
44
|
|
|
101
|
-
|
|
45
|
+
registerShutdown(shutdown);
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
await runtime.start();
|
|
49
|
+
} catch (err) {
|
|
102
50
|
const message = err && err.message ? err.message : String(err);
|
|
103
51
|
console.error(message);
|
|
104
|
-
|
|
105
|
-
try {
|
|
106
|
-
if (watcherStarted) {
|
|
107
|
-
await projectWatcher.close();
|
|
108
|
-
}
|
|
109
|
-
} catch {
|
|
110
|
-
// ignore
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
try {
|
|
114
|
-
liveReload.close();
|
|
115
|
-
} catch {
|
|
116
|
-
// ignore
|
|
117
|
-
}
|
|
118
|
-
|
|
119
52
|
try {
|
|
120
|
-
await
|
|
53
|
+
await runtime.close();
|
|
121
54
|
} catch {
|
|
122
55
|
// ignore
|
|
123
56
|
}
|
|
124
|
-
|
|
125
57
|
// cleanup 完了後に終了する。
|
|
126
58
|
// IPC 接続があると exitCode だけではプロセスが残るため明示終了する。
|
|
127
59
|
process.exit(1);
|
|
128
60
|
}
|
|
129
|
-
|
|
130
|
-
registerShutdown(shutdown);
|
|
131
|
-
|
|
132
|
-
// 初回 build → server → watcher
|
|
133
|
-
await projectWatcher.start({ watchFiles: false });
|
|
134
|
-
watcherStarted = true;
|
|
135
|
-
|
|
136
|
-
try {
|
|
137
|
-
await staticServer.start();
|
|
138
|
-
} catch (err) {
|
|
139
|
-
await failStartup(
|
|
140
|
-
formatListenError(err, {
|
|
141
|
-
projectName: project.name,
|
|
142
|
-
host,
|
|
143
|
-
port,
|
|
144
|
-
kind: '開発',
|
|
145
|
-
})
|
|
146
|
-
);
|
|
147
|
-
return;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
await projectWatcher.startWatching();
|
|
151
|
-
|
|
152
|
-
console.log('[JSKim] 開発サーバーを起動しました。');
|
|
153
|
-
console.log(`プロジェクト: ${project.name}`);
|
|
154
|
-
console.log(`ルート: ${outputDisplay}`);
|
|
155
|
-
console.log(`URL: ${staticServer.url}`);
|
|
156
|
-
console.log(
|
|
157
|
-
`ライブリロード: ${liveReloadEnabled ? '有効' : '無効'}`
|
|
158
|
-
);
|
|
159
|
-
console.log('終了するには Ctrl+C を押してください。');
|
|
160
61
|
}
|
|
161
62
|
|
|
162
63
|
module.exports = {
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
const {
|
|
4
|
-
const { resolveProject } = require('../lib/resolve-project');
|
|
5
|
-
const { createProjectWatcher } = require('../lib/create-project-watcher');
|
|
3
|
+
const { createWatchRuntime } = require('../lib/create-watch-runtime');
|
|
6
4
|
const { registerShutdown } = require('./register-shutdown');
|
|
7
5
|
|
|
8
6
|
/**
|
|
@@ -18,33 +16,14 @@ async function runWatchCommand(options = {}) {
|
|
|
18
16
|
const usageLine =
|
|
19
17
|
options.usageLine || 'npm run watch -- <project-name>';
|
|
20
18
|
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
config,
|
|
19
|
+
const runtime = createWatchRuntime({
|
|
20
|
+
mode: 'watch',
|
|
24
21
|
workspaceRoot,
|
|
25
22
|
projectName: options.projectName,
|
|
26
23
|
commandName: 'watch',
|
|
27
24
|
usageLine,
|
|
28
25
|
});
|
|
29
26
|
|
|
30
|
-
const projectWatcher = createProjectWatcher(project, {
|
|
31
|
-
runInitialBuild: true,
|
|
32
|
-
logChanges: true,
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
projectWatcher.on('ready', ({ displayPaths, debounceMs }) => {
|
|
36
|
-
console.log(`[JSKim] プロジェクトを監視しています: ${project.name}`);
|
|
37
|
-
console.log('パス:');
|
|
38
|
-
for (const display of displayPaths) {
|
|
39
|
-
console.log(`- ${display}`);
|
|
40
|
-
}
|
|
41
|
-
console.log('');
|
|
42
|
-
console.log(`Debounce: ${debounceMs}ms`);
|
|
43
|
-
console.log('終了するには Ctrl+C を押してください。');
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
await projectWatcher.start();
|
|
47
|
-
|
|
48
27
|
let stopping = false;
|
|
49
28
|
|
|
50
29
|
async function shutdown() {
|
|
@@ -54,7 +33,7 @@ async function runWatchCommand(options = {}) {
|
|
|
54
33
|
stopping = true;
|
|
55
34
|
|
|
56
35
|
try {
|
|
57
|
-
await
|
|
36
|
+
await runtime.close();
|
|
58
37
|
} catch {
|
|
59
38
|
// 終了時エラーは無視
|
|
60
39
|
}
|
|
@@ -64,6 +43,17 @@ async function runWatchCommand(options = {}) {
|
|
|
64
43
|
}
|
|
65
44
|
|
|
66
45
|
registerShutdown(shutdown);
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
await runtime.start();
|
|
49
|
+
} catch (err) {
|
|
50
|
+
try {
|
|
51
|
+
await runtime.close();
|
|
52
|
+
} catch {
|
|
53
|
+
// ignore
|
|
54
|
+
}
|
|
55
|
+
throw err;
|
|
56
|
+
}
|
|
67
57
|
}
|
|
68
58
|
|
|
69
59
|
module.exports = {
|
|
@@ -7,23 +7,17 @@ const { createNunjucksEnv } = require('./create-nunjucks-env');
|
|
|
7
7
|
const { cleanOutput } = require('./clean-output');
|
|
8
8
|
const { renderPages } = require('./render-pages');
|
|
9
9
|
const { copyFiles } = require('./copy-files');
|
|
10
|
+
const { processFiles } = require('./process-files');
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* 設定を読み込み、プロジェクトを解決してフルビルドを実行します。
|
|
13
14
|
*
|
|
14
15
|
* @param {string|undefined} projectName
|
|
15
16
|
* @param {object} [options]
|
|
16
|
-
* @param {string} [options.workspaceRoot]
|
|
17
|
-
* @param {string} [options.commandName='build']
|
|
18
|
-
* @param {string} [options.usageLine]
|
|
19
|
-
* @param {string} [options.logTitle='ビルドが完了しました']
|
|
20
|
-
* @param {boolean} [options.includeOutput=true]
|
|
21
|
-
* @param {boolean} [options.log=true]
|
|
22
17
|
* @returns {Promise<object>}
|
|
23
18
|
*/
|
|
24
19
|
async function buildProject(projectName, options = {}) {
|
|
25
|
-
const workspaceRoot =
|
|
26
|
-
options.workspaceRoot || process.cwd();
|
|
20
|
+
const workspaceRoot = options.workspaceRoot || process.cwd();
|
|
27
21
|
const commandName = options.commandName || 'build';
|
|
28
22
|
|
|
29
23
|
const { config } = loadConfig(workspaceRoot);
|
|
@@ -39,7 +33,7 @@ async function buildProject(projectName, options = {}) {
|
|
|
39
33
|
}
|
|
40
34
|
|
|
41
35
|
/**
|
|
42
|
-
* 解決済みプロジェクトに対して clean →
|
|
36
|
+
* 解決済みプロジェクトに対して clean → pipeline を実行します。
|
|
43
37
|
*
|
|
44
38
|
* @param {object} project
|
|
45
39
|
* @param {object} [options]
|
|
@@ -62,10 +56,22 @@ async function runBuild(project, options = {}) {
|
|
|
62
56
|
const env = createNunjucksEnv({
|
|
63
57
|
sourceDir: project.sourceDir,
|
|
64
58
|
templates: project.templates,
|
|
59
|
+
nunjucks: project.nunjucks,
|
|
65
60
|
});
|
|
66
61
|
|
|
67
|
-
|
|
68
|
-
|
|
62
|
+
let renderedCount = 0;
|
|
63
|
+
let copiedCount = 0;
|
|
64
|
+
|
|
65
|
+
if (project.pipelineMode === 'files') {
|
|
66
|
+
const result = await processFiles({ env, project });
|
|
67
|
+
renderedCount = result.renderedCount;
|
|
68
|
+
copiedCount = result.copiedCount;
|
|
69
|
+
} else {
|
|
70
|
+
const rendered = await renderPages({ env, project });
|
|
71
|
+
const copied = await copyFiles({ project });
|
|
72
|
+
renderedCount = rendered.renderedCount;
|
|
73
|
+
copiedCount = copied.copiedCount;
|
|
74
|
+
}
|
|
69
75
|
|
|
70
76
|
const outputDisplay = path
|
|
71
77
|
.relative(project.workspaceRoot, project.outputDir)
|
|
@@ -92,7 +98,7 @@ function printBuildResult(result, options = {}) {
|
|
|
92
98
|
|
|
93
99
|
console.log(`[JSKim] ${logTitle}`);
|
|
94
100
|
console.log(`プロジェクト: ${result.project.name}`);
|
|
95
|
-
console.log(
|
|
101
|
+
console.log(`レンダリングしたファイル数: ${result.renderedCount}`);
|
|
96
102
|
console.log(`コピーしたファイル数: ${result.copiedCount}`);
|
|
97
103
|
if (includeOutput) {
|
|
98
104
|
console.log(`出力先: ${result.outputDisplay}`);
|
|
@@ -7,13 +7,15 @@ const nunjucks = require('nunjucks');
|
|
|
7
7
|
/**
|
|
8
8
|
* プロジェクト用の Nunjucks 環境を作成します。
|
|
9
9
|
* loader ルート: sourceDir + templates[](重複除去)。
|
|
10
|
+
* filters / globals を登録します。
|
|
10
11
|
*
|
|
11
12
|
* @param {object} options
|
|
12
13
|
* @param {string} options.sourceDir
|
|
13
14
|
* @param {string[]} options.templates
|
|
15
|
+
* @param {object} [options.nunjucks]
|
|
14
16
|
* @returns {nunjucks.Environment}
|
|
15
17
|
*/
|
|
16
|
-
function createNunjucksEnv({ sourceDir, templates }) {
|
|
18
|
+
function createNunjucksEnv({ sourceDir, templates, nunjucks: nunjucksConfig }) {
|
|
17
19
|
const roots = [];
|
|
18
20
|
const seen = new Set();
|
|
19
21
|
|
|
@@ -48,14 +50,50 @@ function createNunjucksEnv({ sourceDir, templates }) {
|
|
|
48
50
|
noCache: true,
|
|
49
51
|
});
|
|
50
52
|
|
|
53
|
+
// HTML 互換のため autoescape は維持する。
|
|
54
|
+
// JSON/JS 出力が必要な filter は SafeString を返すこと。
|
|
51
55
|
const env = new nunjucks.Environment(loader, {
|
|
52
56
|
autoescape: true,
|
|
53
57
|
noCache: true,
|
|
54
58
|
});
|
|
55
59
|
|
|
60
|
+
const config =
|
|
61
|
+
nunjucksConfig && typeof nunjucksConfig === 'object' ? nunjucksConfig : {};
|
|
62
|
+
registerFilters(env, config.filters || {});
|
|
63
|
+
registerGlobals(env, config.globals || {});
|
|
64
|
+
|
|
56
65
|
return env;
|
|
57
66
|
}
|
|
58
67
|
|
|
68
|
+
function registerFilters(env, filters) {
|
|
69
|
+
for (const [name, fn] of Object.entries(filters)) {
|
|
70
|
+
env.addFilter(name, wrapSyncCallable(fn, `filter:${name}`));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function registerGlobals(env, globals) {
|
|
75
|
+
for (const [name, value] of Object.entries(globals)) {
|
|
76
|
+
if (typeof value === 'function') {
|
|
77
|
+
env.addGlobal(name, wrapSyncCallable(value, `global:${name}`));
|
|
78
|
+
} else {
|
|
79
|
+
env.addGlobal(name, value);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function wrapSyncCallable(fn, label) {
|
|
85
|
+
return function wrapped(...args) {
|
|
86
|
+
const result = fn.apply(this, args);
|
|
87
|
+
if (result && typeof result.then === 'function') {
|
|
88
|
+
throw new Error(
|
|
89
|
+
`[JSKim] 非同期${label.startsWith('filter') ? 'filter' : 'global'}は現在サポートされていません。\n` +
|
|
90
|
+
`対象: ${label}`
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
return result;
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
59
97
|
module.exports = {
|
|
60
98
|
createNunjucksEnv,
|
|
61
99
|
};
|