@nutin/cli 1.0.1 ā 1.2.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/CHANGELOG.md +58 -0
- package/README.md +47 -1
- package/lib/index.mjs +6 -6
- package/lib/src/file-generator.mjs +11 -6
- package/lib/src/package-manager.mjs +54 -49
- package/lib/src/prompts.mjs +11 -11
- package/lib/src/utils.mjs +2 -1
- package/package.json +2 -1
- package/templates/base/.dockerignore.hbs +10 -1
- package/templates/base/Dockerfile.hbs +28 -8
- package/templates/base/README.md.hbs +23 -0
- package/templates/base/nginx.conf.hbs +43 -0
- package/templates/base/src/app/views/home/home.view.ts.hbs +1 -1
- package/templates/base/src/core/base-classes/base-component/base-component.ts.hbs +2 -1
- package/templates/base/src/core/base-classes/base-component/helpers/catalog.helper.ts.hbs +30 -7
- package/templates/base/src/core/base-classes/base-component/helpers/token.helper.ts.hbs +3 -2
- package/templates/base/src/core/services/event-bus/event-bus.ts.hbs +55 -27
- package/templates/base/src/core/services/http-client/http-client.ts.hbs +24 -17
- package/templates/base/src/core/services/router/helpers/view-render-manager.helper.ts.hbs +25 -10
- package/templates/base/src/core/services/router/router.ts.hbs +1 -0
- package/templates/base/src/index.html.hbs +1 -2
- package/templates/base/src/libs/popover/popover.ts.hbs +2 -1
- package/templates/base/src/styles/main.scss.hbs +2 -1
- package/templates/base/tools/builder/builder.js.hbs +17 -27
- package/templates/base/tools/builder/core/copy-static.js.hbs +16 -20
- package/templates/base/tools/builder/core/esbuild.js.hbs +36 -0
- package/templates/base/tools/builder/core/finalize-build.js.hbs +24 -0
- package/templates/base/tools/builder/core/minify-html.js.hbs +60 -0
- package/templates/base/tools/builder/core/paths.js.hbs +19 -0
- package/templates/base/tools/builder/core/sass.config.js.hbs +16 -9
- package/templates/base/tools/builder/core/tsc.js.hbs +8 -0
- package/templates/base/tools/builder/core/validate-html.js.hbs +26 -11
- package/templates/base/tools/dev.js.hbs +43 -0
- package/templates/base/tools/generator/generator.js.hbs +9 -2
- package/templates/base/tools/generator/handle-file.js.hbs +23 -0
- package/templates/base/tools/generator/templates/index.js.hbs +3 -0
- package/templates/base/tools/serve.js.hbs +23 -0
- package/templates/base/tools/utils/export-flags.js.hbs +5 -0
- package/templates/base/tools/utils/get-files-recursive.js.hbs +1 -3
- package/templates/base/tools/utils/index.js.hbs +5 -1
- package/templates/base/tools/utils/prompt-boolean.js.hbs +15 -0
- package/templates/base/tools/utils/run-command.js.hbs +5 -0
- package/templates/base/tools/utils/run-script.js.hbs +22 -0
- package/templates/base/tools/watcher.js.hbs +44 -0
- package/templates/features/i18n/tools/builder/core/build-i18n.js.hbs +11 -16
- package/templates/features/stylinNutin/src/styles/_custom-classes.scss.hbs +1 -1
- package/templates/features/stylinNutin/src/styles/components/_index.scss.hbs +3 -0
- package/templates/features/stylinNutin/tools/generator/templates/scss.template.js.hbs +4 -0
- package/templates/features/template/src/app/views/home/home.view.html.hbs +1 -1
- package/templates/features/template/tools/builder/core/merge-templates.js.hbs +13 -26
- package/templates/features/template/tools/watcher.js.hbs +0 -27
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
import * as sass from 'sass';
|
|
2
2
|
import * as fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
3
4
|
import { print } from '../../utils/index.js';
|
|
4
|
-
{
|
|
5
|
-
const stylesOutput = 'dist-build/src'
|
|
6
|
-
{{else}}
|
|
7
|
-
const stylesOutput = 'dist/src';
|
|
8
|
-
{{/if}}
|
|
9
|
-
if (!fs.existsSync(stylesOutput)) fs.mkdirSync(stylesOutput);
|
|
5
|
+
import { PATHS } from './paths.js';
|
|
10
6
|
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
const stylesInput = path.join(PATHS.source, 'styles');
|
|
8
|
+
const stylesOutput = path.join(PATHS.tempSource, 'main.css');
|
|
9
|
+
|
|
10
|
+
const scssPath = (origin) => path.join(stylesInput, origin);
|
|
11
|
+
const scssOrigins = [
|
|
12
|
+
'base',
|
|
13
|
+
'core'{{#if stylinNutin}},
|
|
14
|
+
'components'{{/if}}
|
|
15
|
+
];
|
|
16
|
+
const pathsToLoad = scssOrigins.map(origin => scssPath(origin));
|
|
17
|
+
|
|
18
|
+
sass.compileAsync(path.join(stylesInput, 'main.scss'), {
|
|
19
|
+
loadPaths: [ ...pathsToLoad ],
|
|
13
20
|
style: 'compressed'
|
|
14
21
|
}).then(result => {
|
|
22
|
+
fs.writeFileSync(stylesOutput, result.css);
|
|
15
23
|
print.info('Global styles compiled.');
|
|
16
|
-
fs.writeFileSync(`${stylesOutput}/main.css`, result.css);
|
|
17
24
|
});
|
|
@@ -1,18 +1,35 @@
|
|
|
1
|
-
import { readFile } from 'fs/promises';
|
|
1
|
+
import { readFile, writeFile } from 'fs/promises';
|
|
2
|
+
import { JSDOM } from 'jsdom';
|
|
2
3
|
import { exit } from 'process';
|
|
3
|
-
import { print } from '../../utils/index.js';
|
|
4
|
+
import { print, isProd } from '../../utils/index.js';
|
|
5
|
+
import { PATHS } from './paths.js';
|
|
6
|
+
import path from 'path';
|
|
4
7
|
|
|
5
8
|
function errorExit(message) {
|
|
6
9
|
print.boldError(`[ERROR] ${message}`);
|
|
7
10
|
exit(1);
|
|
8
11
|
}
|
|
9
12
|
|
|
13
|
+
async function addEntrypoint(htmlContent, filePath) {
|
|
14
|
+
print.info('Adding script in HTML...');
|
|
15
|
+
|
|
16
|
+
const dom = new JSDOM(htmlContent);
|
|
17
|
+
const document = dom.window.document;
|
|
18
|
+
|
|
19
|
+
const newScript = document.createElement("script");
|
|
20
|
+
newScript.type = "module";
|
|
21
|
+
newScript.src = isProd ? "/bundle.js" : "/app/main.js";
|
|
22
|
+
|
|
23
|
+
document.body.appendChild(newScript);
|
|
24
|
+
const serialized = dom.serialize().replace(/>\s+</g, '><')
|
|
25
|
+
|
|
26
|
+
writeFile(filePath, serialized, "utf8");
|
|
27
|
+
|
|
28
|
+
return htmlContent;
|
|
29
|
+
}
|
|
30
|
+
|
|
10
31
|
async function validateHtml() {
|
|
11
|
-
|
|
12
|
-
const filePath = 'dist-build/src/index.html';
|
|
13
|
-
{{else}}
|
|
14
|
-
const filePath = 'dist/src/index.html';
|
|
15
|
-
{{/if}}
|
|
32
|
+
const filePath = path.join(PATHS.tempSource, 'index.html');
|
|
16
33
|
|
|
17
34
|
let htmlContent;
|
|
18
35
|
try {
|
|
@@ -25,14 +42,12 @@ async function validateHtml() {
|
|
|
25
42
|
errorExit('Missing #app container in HTML');
|
|
26
43
|
}
|
|
27
44
|
|
|
28
|
-
if (!/<script[^>]*type=["']module["']/.test(htmlContent)) {
|
|
29
|
-
errorExit('No module scripts found in HTML');
|
|
30
|
-
}
|
|
31
|
-
|
|
32
45
|
if (!/<link[^>]*rel=["']stylesheet["']/.test(htmlContent)) {
|
|
33
46
|
errorExit('No stylesheet found in HTML');
|
|
34
47
|
}
|
|
35
48
|
|
|
49
|
+
await addEntrypoint(htmlContent, filePath);
|
|
50
|
+
|
|
36
51
|
print.info('HTML validation passed');
|
|
37
52
|
}
|
|
38
53
|
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { spawn } from 'child_process';
|
|
4
|
+
import { print, runCommand } from './utils/index.js';
|
|
5
|
+
|
|
6
|
+
function run(command, args = [], label = '') {
|
|
7
|
+
return new Promise((resolve, reject) => {
|
|
8
|
+
print.info(`\nā¶ ${label || command}...`);
|
|
9
|
+
|
|
10
|
+
const child = spawn(command, args, { stdio: 'inherit', shell: true });
|
|
11
|
+
|
|
12
|
+
child.on('close', (code) => {
|
|
13
|
+
if (code === 0) resolve();
|
|
14
|
+
else reject(new Error(`${command} exited with code ${code}`));
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async function startDev() {
|
|
20
|
+
try {
|
|
21
|
+
console.clear();
|
|
22
|
+
print.boldHead('š Starting Dev Environment...');
|
|
23
|
+
|
|
24
|
+
await run('{{packageManager}}', ['run', 'build'], 'Running initial build');
|
|
25
|
+
|
|
26
|
+
const serve = spawn('{{packageManager}}', ['run', 'serve:only'], { stdio: 'inherit', shell: true });
|
|
27
|
+
const watcher = spawn('node', ['tools/watcher.js'], { stdio: 'inherit', shell: true });
|
|
28
|
+
|
|
29
|
+
print.boldSuccess('\nDev server and watcher running!\n');
|
|
30
|
+
|
|
31
|
+
serve.on('close', (code) => {
|
|
32
|
+
print.error(`live-server exited with code ${code}`);
|
|
33
|
+
process.exit(code);
|
|
34
|
+
});
|
|
35
|
+
watcher.on('close', (code) => {
|
|
36
|
+
print.error(`watcher exited with code ${code}`);
|
|
37
|
+
});
|
|
38
|
+
} catch (err) {
|
|
39
|
+
print.boldError(`Dev startup failed: ${err.message}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
startDev();
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import { allFormats, getLastWord, print } from "../utils/index.js";
|
|
4
|
-
import { generateFile, appendToIndex{{#if i18n}}, generateJson{{/if}} } from "./handle-file.js";
|
|
3
|
+
import { allFormats, getLastWord, print{{#if stylinNutin}}, promptBoolean{{/if}} } from "../utils/index.js";
|
|
4
|
+
import { generateFile, appendToIndex{{#if i18n}}, generateJson{{/if}}{{#if stylinNutin}}, generateStylesheet{{/if}} } from "./handle-file.js";
|
|
5
5
|
import { serviceTemplate, componentTemplate, viewTemplate{{#if template}}, htmlTemplate{{/if}} } from "./templates/index.js";
|
|
6
6
|
|
|
7
7
|
// Constants and Setup
|
|
@@ -29,6 +29,10 @@ const creators = {
|
|
|
29
29
|
},
|
|
30
30
|
component: (name, targetPath) => {
|
|
31
31
|
print.section(`Creating component: ${name.capitalized}`);
|
|
32
|
+
{{#if stylinNutin}}
|
|
33
|
+
const doCreateStylesheet = await promptBoolean('Create component\'s stylesheet in styles/components ?');
|
|
34
|
+
{{/if}}
|
|
35
|
+
|
|
32
36
|
try {
|
|
33
37
|
generateFile({ name, targetPath, templateFn: componentTemplate, suffix: 'component' });
|
|
34
38
|
{{#if template}}
|
|
@@ -37,6 +41,9 @@ const creators = {
|
|
|
37
41
|
{{#if i18n}}
|
|
38
42
|
generateJson({ targetPath, name });
|
|
39
43
|
{{/if}}
|
|
44
|
+
{{#if stylinNutin}}
|
|
45
|
+
if (doCreateStylesheet) generateStylesheet(name);
|
|
46
|
+
{{/if}}
|
|
40
47
|
appendToIndex({ name, targetPath, suffix: 'component' });
|
|
41
48
|
} catch (err) {
|
|
42
49
|
handleError("Failed to generate component", err);
|
|
@@ -59,4 +59,27 @@ export function generateJson({ targetPath, name }) {
|
|
|
59
59
|
});
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
+
{{/if}}
|
|
63
|
+
{{#if stylinNutin}}
|
|
64
|
+
export function generateStylesheet(name) {
|
|
65
|
+
const template = scssTemplate();
|
|
66
|
+
const componentsStylePath = path.join('src', 'styles', 'components');
|
|
67
|
+
const filePath = path.join(componentsStylePath, `_${name.kebab}.scss`);
|
|
68
|
+
const indexPath = path.join(componentsStylePath, `_index.scss`);
|
|
69
|
+
|
|
70
|
+
if (fs.existsSync(filePath)) {
|
|
71
|
+
print.boldError('A file with this name already exists');
|
|
72
|
+
process.exit(1);
|
|
73
|
+
}
|
|
74
|
+
fs.writeFileSync(filePath, template);
|
|
75
|
+
|
|
76
|
+
const lineToAppend = `@forward "${name.kebab}";\n`;
|
|
77
|
+
|
|
78
|
+
try {
|
|
79
|
+
fs.appendFileSync(indexPath, lineToAppend, 'utf8');
|
|
80
|
+
} catch (err) {
|
|
81
|
+
print.error(`Error appending line: ${err}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
62
85
|
{{/if}}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import liveServer from 'live-server';
|
|
4
|
+
|
|
5
|
+
const params = {
|
|
6
|
+
port: 9090,
|
|
7
|
+
root: 'dist/src',
|
|
8
|
+
open: false, // set to true to open a new window in browser
|
|
9
|
+
logLevel: 0, // 0 = errors only, 1 = some info, 2 = verbose
|
|
10
|
+
entryFile: 'index.html',
|
|
11
|
+
wait: 100, // debounce reloads
|
|
12
|
+
middleware: [
|
|
13
|
+
function(req, res, next) {
|
|
14
|
+
if (req.url.includes('.') && !req.url.endsWith('/')) {
|
|
15
|
+
return next();
|
|
16
|
+
}
|
|
17
|
+
req.url = '/index.html';
|
|
18
|
+
next();
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
liveServer.start(params);
|
|
@@ -2,7 +2,7 @@ import * as fs from 'fs';
|
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
|
|
4
4
|
export function getFilesRecursive(dir, extension) {
|
|
5
|
-
const fullDir = path.resolve(
|
|
5
|
+
const fullDir = path.resolve(dir);
|
|
6
6
|
|
|
7
7
|
if (!fs.existsSync(fullDir)) return [];
|
|
8
8
|
|
|
@@ -14,10 +14,8 @@ export function getFilesRecursive(dir, extension) {
|
|
|
14
14
|
const fullPath = path.join(currentDir, item.name);
|
|
15
15
|
|
|
16
16
|
if (item.isDirectory()) {
|
|
17
|
-
// Recursively search subdirectories
|
|
18
17
|
files.push(...findFilesRecursively(fullPath));
|
|
19
18
|
} else if (item.isFile() && item.name.endsWith(extension)) {
|
|
20
|
-
// Add files to the results
|
|
21
19
|
files.push(fullPath);
|
|
22
20
|
}
|
|
23
21
|
}
|
|
@@ -1,3 +1,7 @@
|
|
|
1
1
|
export * from './print.js';
|
|
2
2
|
export * from './format.js';
|
|
3
|
-
export * from './get-files-recursive.js';
|
|
3
|
+
export * from './get-files-recursive.js';
|
|
4
|
+
export * from './run-command.js';
|
|
5
|
+
export * from './run-script.js';
|
|
6
|
+
export * from './export-flags.js';
|
|
7
|
+
export * from './prompt-boolean.js';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import readline from 'readline';
|
|
2
|
+
|
|
3
|
+
export const promptBoolean = (question) => {
|
|
4
|
+
const rl = readline.createInterface({
|
|
5
|
+
input: process.stdin,
|
|
6
|
+
output: process.stdout
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
return new Promise((resolve) => {
|
|
10
|
+
rl.question(`${question} (y/n): `, (answer) => {
|
|
11
|
+
rl.close();
|
|
12
|
+
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { resolve } from 'path';
|
|
2
|
+
import { spawnSync } from 'child_process';
|
|
3
|
+
import process from 'process';
|
|
4
|
+
import { print } from './index.js';
|
|
5
|
+
|
|
6
|
+
export function runScript(path, message) {
|
|
7
|
+
print.boldSection(`\n${message}`);
|
|
8
|
+
|
|
9
|
+
const args = [resolve(path)];
|
|
10
|
+
|
|
11
|
+
const result = spawnSync('node', args,
|
|
12
|
+
{
|
|
13
|
+
stdio: 'inherit',
|
|
14
|
+
env: { ...process.env },
|
|
15
|
+
}
|
|
16
|
+
);
|
|
17
|
+
|
|
18
|
+
if (result.status !== 0) {
|
|
19
|
+
print.boldError(`\nScript ${path} failed.`);
|
|
20
|
+
process.exit(result.status ?? 1);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import chokidar from 'chokidar';
|
|
2
|
+
import { exec } from 'child_process';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import { print } from './utils/index.js'
|
|
5
|
+
|
|
6
|
+
const watcher = chokidar.watch(['src'], {
|
|
7
|
+
ignored: /(^|[/\\])\../,
|
|
8
|
+
persistent: true,
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
let isBuilding = false;
|
|
12
|
+
let buildTimeout = null;
|
|
13
|
+
|
|
14
|
+
watcher.on('change', (filePath) => {
|
|
15
|
+
if (buildTimeout) {
|
|
16
|
+
clearTimeout(buildTimeout);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
buildTimeout = setTimeout(() => {
|
|
20
|
+
if (isBuilding) return;
|
|
21
|
+
isBuilding = true;
|
|
22
|
+
|
|
23
|
+
console.clear();
|
|
24
|
+
print.boldInfo(`\nš File changed: ${path.relative(process.cwd(), filePath)}\n`);
|
|
25
|
+
print.info('\nRebuilding...');
|
|
26
|
+
|
|
27
|
+
const command = '{{packageManager}} run build';
|
|
28
|
+
|
|
29
|
+
exec(command, (err, stdout, stderr) => {
|
|
30
|
+
if (stdout) process.stdout.write(stdout);
|
|
31
|
+
if (stderr) process.stderr.write(stderr);
|
|
32
|
+
if (err) {
|
|
33
|
+
print.error(`\nā Build failed: ${err.message}`);
|
|
34
|
+
} else {
|
|
35
|
+
print.boldHead('Watching for changes...');
|
|
36
|
+
}
|
|
37
|
+
isBuilding = false;
|
|
38
|
+
});
|
|
39
|
+
}, 100);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
watcher.on('ready', () => {
|
|
43
|
+
print.boldHead('\nWatching for changes...\n');
|
|
44
|
+
});
|
|
@@ -1,19 +1,13 @@
|
|
|
1
1
|
import * as fs from 'fs';
|
|
2
2
|
import * as path from 'path';
|
|
3
3
|
import { exit } from 'process';
|
|
4
|
-
import { getFilesRecursive, print } from '../../utils/index.js';
|
|
4
|
+
import { getFilesRecursive, print, isVerbose } from '../../utils/index.js';
|
|
5
|
+
import { PATHS } from './paths.js';
|
|
5
6
|
|
|
6
|
-
const
|
|
7
|
-
{{#if template}}
|
|
8
|
-
const destDir = './dist-build/src/locales';
|
|
9
|
-
const cleanupDir = './dist-build/src/app';
|
|
10
|
-
{{else}}
|
|
11
|
-
const destDir = './dist/src/locales';
|
|
12
|
-
const cleanupDir = './dist/src/app';
|
|
13
|
-
{{/if}}
|
|
7
|
+
const destDir = path.join(PATHS.tempSource, 'locales');
|
|
14
8
|
|
|
15
9
|
function findLocaleFiles() {
|
|
16
|
-
const allFiles = getFilesRecursive(
|
|
10
|
+
const allFiles = getFilesRecursive(PATHS.sourceApp, '.json');
|
|
17
11
|
const localeFileMap = {};
|
|
18
12
|
|
|
19
13
|
allFiles.forEach(file => {
|
|
@@ -66,18 +60,19 @@ async function removeJsonFiles(directory) {
|
|
|
66
60
|
if (file.includes(`${path.sep}locales${path.sep}`)) {
|
|
67
61
|
try {
|
|
68
62
|
await fs.promises.unlink(file);
|
|
69
|
-
print.info(`Deleted locale JSON file: ${file}`);
|
|
63
|
+
if (isVerbose) print.info(`Deleted locale JSON file: ${file}`);
|
|
70
64
|
} catch (err) {
|
|
71
65
|
print.error(`Failed to remove ${file}: ${err.message}`);
|
|
72
66
|
}
|
|
73
|
-
}
|
|
67
|
+
}
|
|
68
|
+
else if (isVerbose) {
|
|
74
69
|
print.info(`Skipping non-locale JSON file: ${file}`);
|
|
75
70
|
}
|
|
76
71
|
}
|
|
77
72
|
|
|
78
73
|
await removeEmptyDirectories(directory);
|
|
79
74
|
|
|
80
|
-
print.info(`ā
JSON files cleanup complete.`);
|
|
75
|
+
if (isVerbose) print.info(`ā
JSON files cleanup complete.`);
|
|
81
76
|
}
|
|
82
77
|
|
|
83
78
|
async function removeEmptyDirectories(directory) {
|
|
@@ -102,12 +97,12 @@ async function removeEmptyDirectories(directory) {
|
|
|
102
97
|
}
|
|
103
98
|
}
|
|
104
99
|
|
|
105
|
-
if (directory !==
|
|
100
|
+
if (directory !== PATHS.tempApp) {
|
|
106
101
|
try {
|
|
107
102
|
const updatedItems = await fs.promises.readdir(directory);
|
|
108
103
|
if (updatedItems.length === 0) {
|
|
109
104
|
await fs.promises.rmdir(directory, { recursive: false });
|
|
110
|
-
print.info(`š Removed empty directory: ${directory}`);
|
|
105
|
+
if (isVerbose) print.info(`š Removed empty directory: ${directory}`);
|
|
111
106
|
}
|
|
112
107
|
} catch (err) {
|
|
113
108
|
print.error(`Failed to remove empty directory ${directory}: ${err.message}`);
|
|
@@ -128,7 +123,7 @@ async function mergeJson() {
|
|
|
128
123
|
);
|
|
129
124
|
print.info(`ā
Json files for locale ${locale} merged successfully`);
|
|
130
125
|
}
|
|
131
|
-
removeJsonFiles(
|
|
126
|
+
removeJsonFiles(PATHS.tempApp);
|
|
132
127
|
}
|
|
133
128
|
|
|
134
129
|
mergeJson().catch((err) => {
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
{{else}}
|
|
22
22
|
style="font-style:italic;font-size:0.75rem;"
|
|
23
23
|
{{/if}}
|
|
24
|
-
>nutin
|
|
24
|
+
>nutin v{{version}}</span>
|
|
25
25
|
<img src="/assets/images/logo-trans.avif" width=250 height=250 alt="nutin's logo. A frame opened on the top left through which a plant grows.">
|
|
26
26
|
<div
|
|
27
27
|
{{#if i18n}}
|
|
@@ -1,52 +1,38 @@
|
|
|
1
1
|
import fs from 'fs/promises';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { exit } from 'process';
|
|
4
|
-
import { print } from '../../utils/index.js';
|
|
4
|
+
import { getFilesRecursive, print, isVerbose, isProd } from '../../utils/index.js';
|
|
5
|
+
import { minifyHTML } from './minify-html.js';
|
|
6
|
+
import { PATHS } from './paths.js';
|
|
5
7
|
|
|
6
|
-
const JS_DIR = 'dist-build/src/app';
|
|
7
8
|
const PLACEHOLDER = '__TEMPLATE_PLACEHOLDER__';
|
|
8
9
|
|
|
9
|
-
async function findHtmlFiles(dir) {
|
|
10
|
-
const entries = await fs.readdir(dir, { withFileTypes: true });
|
|
11
|
-
const files = await Promise.all(
|
|
12
|
-
entries.map(async (entry) => {
|
|
13
|
-
const fullPath = path.join(dir, entry.name);
|
|
14
|
-
if (entry.isDirectory()) {
|
|
15
|
-
return findHtmlFiles(fullPath);
|
|
16
|
-
} else if (entry.isFile() && fullPath.endsWith('.html')) {
|
|
17
|
-
return [fullPath];
|
|
18
|
-
}
|
|
19
|
-
return [];
|
|
20
|
-
})
|
|
21
|
-
);
|
|
22
|
-
return files.flat();
|
|
23
|
-
}
|
|
24
|
-
|
|
25
10
|
async function mergeTemplates() {
|
|
26
|
-
const htmlFiles = await
|
|
11
|
+
const htmlFiles = await getFilesRecursive(PATHS.tempApp, 'html');
|
|
12
|
+
const fileExtension = isProd ? '.ts' : '.js';
|
|
27
13
|
|
|
28
14
|
for (const htmlPath of htmlFiles) {
|
|
29
|
-
const jsPath = htmlPath.replace(/\.html$/,
|
|
15
|
+
const jsPath = htmlPath.replace(/\.html$/, fileExtension);
|
|
30
16
|
const htmlFilename = path.basename(htmlPath);
|
|
31
17
|
const jsFilename = path.basename(jsPath);
|
|
32
18
|
|
|
33
19
|
try {
|
|
34
20
|
const htmlContent = await fs.readFile(htmlPath, 'utf-8');
|
|
35
|
-
const
|
|
36
|
-
|
|
21
|
+
const minifiedHtml = await minifyHTML(htmlContent);
|
|
22
|
+
if (isVerbose) print.info(`Minified HTML: ${file}`);
|
|
37
23
|
|
|
38
24
|
try {
|
|
39
25
|
let jsContent = await fs.readFile(jsPath, 'utf-8');
|
|
40
26
|
if (!jsContent.includes(PLACEHOLDER)) {
|
|
41
27
|
print.boldError(`WARNING: No matching placeholder in ${jsFilename}`);
|
|
42
|
-
print.error(`
|
|
43
|
-
print.error(`If using inline templates, remove ${htmlFilename}.`);
|
|
28
|
+
print.error(`Make sure the template const is \`${PLACEHOLDER}\`.`);
|
|
44
29
|
exit(1);
|
|
45
30
|
}
|
|
46
31
|
|
|
47
|
-
jsContent = jsContent.replace(PLACEHOLDER,
|
|
32
|
+
jsContent = jsContent.replace(PLACEHOLDER, minifiedHtml);
|
|
48
33
|
await fs.writeFile(jsPath, jsContent);
|
|
49
|
-
|
|
34
|
+
await fs.unlink(htmlPath);
|
|
35
|
+
if (isVerbose) print.info(`ā
Updated ${jsFilename} with template from ${htmlFilename}.`);
|
|
50
36
|
} catch (err) {
|
|
51
37
|
print.boldError(`ERROR: Cannot update ${jsFilename}. ${err.message}`);
|
|
52
38
|
}
|
|
@@ -54,6 +40,7 @@ async function mergeTemplates() {
|
|
|
54
40
|
print.boldError(`ERROR: Failed to read ${htmlFilename}. ${err.message}`);
|
|
55
41
|
}
|
|
56
42
|
}
|
|
43
|
+
print.info(`HTML templates minified and merged in scripts.`)
|
|
57
44
|
}
|
|
58
45
|
|
|
59
46
|
mergeTemplates().catch((err) => {
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import chokidar from 'chokidar';
|
|
2
|
-
import { exec } from 'child_process';
|
|
3
|
-
import path from 'path';
|
|
4
|
-
import { print } from './utils/index.js'
|
|
5
|
-
|
|
6
|
-
const watcher = chokidar.watch(['src'], {
|
|
7
|
-
ignored: /(^|[/\\])\../,
|
|
8
|
-
persistent: true,
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
let isBuilding = false;
|
|
12
|
-
|
|
13
|
-
watcher.on('change', (filePath) => {
|
|
14
|
-
if (isBuilding) return;
|
|
15
|
-
isBuilding = true;
|
|
16
|
-
|
|
17
|
-
console.clear();
|
|
18
|
-
print.boldInfo(`\nš File changed: ${path.relative(process.cwd(), filePath)}\n`);
|
|
19
|
-
print.info('\nRebuilding...')
|
|
20
|
-
|
|
21
|
-
exec('npm run build', (err, stdout, stderr) => {
|
|
22
|
-
if (stderr) process.stderr.write(stderr);
|
|
23
|
-
print.boldHead('\nWaiting for changes...');
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
isBuilding = false;
|
|
27
|
-
});
|