@zulaica/site-bundler 0.4.3
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/.eslintrc.json +19 -0
- package/.gitattributes +7 -0
- package/.github/workflows/lint.yml +17 -0
- package/.prettierrc.json +7 -0
- package/.vscode/extensions.json +11 -0
- package/.vscode/settings.json +23 -0
- package/README.md +13 -0
- package/lib/helpers/constants.mjs +74 -0
- package/lib/helpers/index.mjs +4 -0
- package/lib/helpers/loggers.mjs +16 -0
- package/lib/index.mjs +60 -0
- package/lib/modules/copyAssets.mjs +36 -0
- package/lib/modules/index.mjs +7 -0
- package/lib/modules/preflight.mjs +31 -0
- package/lib/modules/processCSS.mjs +33 -0
- package/lib/modules/processHTML.mjs +25 -0
- package/lib/modules/processJS.mjs +41 -0
- package/package.json +56 -0
package/.eslintrc.json
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
{
|
|
2
|
+
"env": {
|
|
3
|
+
"es2022": true,
|
|
4
|
+
"node": true
|
|
5
|
+
},
|
|
6
|
+
"extends": [
|
|
7
|
+
"eslint:recommended",
|
|
8
|
+
"plugin:prettier/recommended"
|
|
9
|
+
],
|
|
10
|
+
"parserOptions": {
|
|
11
|
+
"sourceType": "module"
|
|
12
|
+
},
|
|
13
|
+
"plugins": [
|
|
14
|
+
"prettier"
|
|
15
|
+
],
|
|
16
|
+
"rules": {
|
|
17
|
+
"no-var": "error"
|
|
18
|
+
}
|
|
19
|
+
}
|
package/.gitattributes
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
name: Lint Code Base
|
|
2
|
+
|
|
3
|
+
on: pull_request
|
|
4
|
+
|
|
5
|
+
jobs:
|
|
6
|
+
lint_javascript:
|
|
7
|
+
name: Lint JavaScript Files
|
|
8
|
+
runs-on: ubuntu-latest
|
|
9
|
+
steps:
|
|
10
|
+
- name: Check out branch
|
|
11
|
+
uses: actions/checkout@v3
|
|
12
|
+
- name: Install Node.js
|
|
13
|
+
uses: volta-cli/action@v1
|
|
14
|
+
- name: Install dependencies
|
|
15
|
+
run: npm ci
|
|
16
|
+
- name: Run lint script
|
|
17
|
+
run: npm run lint
|
package/.prettierrc.json
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
|
|
3
|
+
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
|
|
4
|
+
// List of extensions which should be recommended for users of this workspace.
|
|
5
|
+
"recommendations": [
|
|
6
|
+
"dbaeumer.vscode-eslint",
|
|
7
|
+
"esbenp.prettier-vscode"
|
|
8
|
+
],
|
|
9
|
+
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
|
|
10
|
+
"unwantedRecommendations": []
|
|
11
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"editor.codeActionsOnSave": {
|
|
3
|
+
"source.organizeImports": true
|
|
4
|
+
},
|
|
5
|
+
"editor.formatOnSave": true,
|
|
6
|
+
"eslint.run": "onSave",
|
|
7
|
+
"files.exclude": {
|
|
8
|
+
"**/.git": true,
|
|
9
|
+
"**/node_modules": true,
|
|
10
|
+
"**/.DS_Store": true,
|
|
11
|
+
"**/Thumbs.db": true
|
|
12
|
+
},
|
|
13
|
+
"search.exclude": {},
|
|
14
|
+
"[javascript]": {
|
|
15
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
16
|
+
},
|
|
17
|
+
"[json]": {
|
|
18
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
|
19
|
+
},
|
|
20
|
+
"[jsonc]": {
|
|
21
|
+
"editor.defaultFormatter": "vscode.json-language-features"
|
|
22
|
+
}
|
|
23
|
+
}
|
package/README.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
export const OPTIONS = Object.freeze({
|
|
2
|
+
babel: {
|
|
3
|
+
presets: [
|
|
4
|
+
[
|
|
5
|
+
'@babel/env',
|
|
6
|
+
{
|
|
7
|
+
targets: 'defaults',
|
|
8
|
+
modules: false
|
|
9
|
+
}
|
|
10
|
+
]
|
|
11
|
+
],
|
|
12
|
+
babelrc: false,
|
|
13
|
+
comments: false,
|
|
14
|
+
configFile: false,
|
|
15
|
+
minified: true
|
|
16
|
+
},
|
|
17
|
+
postcss: {
|
|
18
|
+
cssnano: {
|
|
19
|
+
preset: [
|
|
20
|
+
'advanced',
|
|
21
|
+
{
|
|
22
|
+
minifyFontValues: false,
|
|
23
|
+
normalizeUrl: false,
|
|
24
|
+
normalizeString: {
|
|
25
|
+
preferredQuote: 'single'
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
url: [
|
|
31
|
+
{
|
|
32
|
+
filter: '**/*.woff2',
|
|
33
|
+
url: 'rebase'
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
filter: '**/*.woff',
|
|
37
|
+
url: 'rebase'
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
filter: '**/*.ttf',
|
|
41
|
+
url: 'rebase'
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
filter: '**/*.webp',
|
|
45
|
+
url: (asset) => asset.relativePath
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
filter: '**/*.jpg',
|
|
49
|
+
url: (asset) => asset.relativePath
|
|
50
|
+
}
|
|
51
|
+
]
|
|
52
|
+
},
|
|
53
|
+
posthtml: {
|
|
54
|
+
htmlnano: {
|
|
55
|
+
collapseWhitespace: 'aggressive',
|
|
56
|
+
minifyJs: false,
|
|
57
|
+
removeComments: 'all',
|
|
58
|
+
removeEmptyAttributes: false
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
export const EMOJI = Object.freeze({
|
|
64
|
+
artistPalette: '\uD83C\uDFA8',
|
|
65
|
+
barChart: '\uD83D\uDCCA',
|
|
66
|
+
cardIndexDividers: '\uD83D\uDDC2',
|
|
67
|
+
construction: '\uD83D\uDEA7',
|
|
68
|
+
constructionWorker: '\uD83D\uDC77',
|
|
69
|
+
fileCabinet: '\uD83D\uDDC4 ',
|
|
70
|
+
fileFolder: '\uD83D\uDCC1',
|
|
71
|
+
noEntry: '\u26D4\uFE0F',
|
|
72
|
+
package: '\uD83D\uDCE6',
|
|
73
|
+
wastebasket: '\ud83d\uddd1\ufe0f '
|
|
74
|
+
});
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { exit, stderr, stdout } from 'process';
|
|
2
|
+
import { cursorTo } from 'readline';
|
|
3
|
+
import { EMOJI } from './index.mjs';
|
|
4
|
+
|
|
5
|
+
export const logError = (error) => {
|
|
6
|
+
stderr.write(`\n${EMOJI.noEntry} An error has occurred\n`);
|
|
7
|
+
stderr.write(error.toString());
|
|
8
|
+
exit(1);
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const logMessage = (message, update = false) => {
|
|
12
|
+
if (update) {
|
|
13
|
+
cursorTo(stdout, 0, null);
|
|
14
|
+
}
|
|
15
|
+
stdout.write(message);
|
|
16
|
+
};
|
package/lib/index.mjs
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander';
|
|
4
|
+
import { readFile } from 'fs/promises';
|
|
5
|
+
import { createRequire } from 'module';
|
|
6
|
+
import { extname } from 'path';
|
|
7
|
+
import posthtml from 'posthtml';
|
|
8
|
+
import { EMOJI, logError, logMessage } from './helpers/index.mjs';
|
|
9
|
+
import {
|
|
10
|
+
copyAssets,
|
|
11
|
+
preflight,
|
|
12
|
+
processCSS,
|
|
13
|
+
processHTML,
|
|
14
|
+
processJS
|
|
15
|
+
} from './modules/index.mjs';
|
|
16
|
+
|
|
17
|
+
const require = createRequire(import.meta.url);
|
|
18
|
+
const { version } = require('../package.json');
|
|
19
|
+
|
|
20
|
+
(async () => {
|
|
21
|
+
const program = new Command();
|
|
22
|
+
program
|
|
23
|
+
.version(version)
|
|
24
|
+
.requiredOption('-i, --input <input>', 'The source directory')
|
|
25
|
+
.requiredOption('-o, --output <output>', 'The output directory')
|
|
26
|
+
.parse();
|
|
27
|
+
|
|
28
|
+
const { input, output } = program.opts();
|
|
29
|
+
const files = { html: 'index.html', scripts: 'scripts', styles: 'styles' };
|
|
30
|
+
const data = await readFile(`${input}/index.html`, { encoding: 'utf8' });
|
|
31
|
+
posthtml()
|
|
32
|
+
.process(data)
|
|
33
|
+
.then(({ tree }) => {
|
|
34
|
+
tree.match(
|
|
35
|
+
[{ tag: 'script' }, { tag: 'link', attrs: { rel: 'stylesheet' } }],
|
|
36
|
+
(node) => {
|
|
37
|
+
if (node.attrs?.src || node.attrs?.href) {
|
|
38
|
+
const file = node.attrs.src ?? node.attrs.href;
|
|
39
|
+
const extension = extname(file).slice(1);
|
|
40
|
+
files[extension] = file;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
try {
|
|
47
|
+
logMessage(`${EMOJI.constructionWorker} Site Bundler v${version}\n\n`);
|
|
48
|
+
logMessage(`${EMOJI.construction} Bundling release\n`);
|
|
49
|
+
await preflight(output);
|
|
50
|
+
await copyAssets(input, output, files);
|
|
51
|
+
await processCSS(input, output, files.css);
|
|
52
|
+
await processJS(input, output, files.js);
|
|
53
|
+
await processHTML(input, output, files.html);
|
|
54
|
+
logMessage(`${EMOJI.package} Finished!`);
|
|
55
|
+
} catch (error) {
|
|
56
|
+
logError(error);
|
|
57
|
+
} finally {
|
|
58
|
+
logMessage('\n\n');
|
|
59
|
+
}
|
|
60
|
+
})();
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { cp, readdir } from 'fs/promises';
|
|
2
|
+
import { EMOJI, logError, logMessage } from '../helpers/index.mjs';
|
|
3
|
+
|
|
4
|
+
const copyAssets = async (input, output, files) => {
|
|
5
|
+
const assets = await readdir(input);
|
|
6
|
+
for (const file in files) {
|
|
7
|
+
assets.splice(assets.indexOf(files[file]), 1);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
if (!assets.length) {
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
try {
|
|
15
|
+
logMessage(
|
|
16
|
+
`${EMOJI.cardIndexDividers} Copying assets (0/${assets.length})`
|
|
17
|
+
);
|
|
18
|
+
for (const [index, asset] of assets.entries()) {
|
|
19
|
+
await cp(`${input}/${asset}`, `${output}/${asset}`, {
|
|
20
|
+
recursive: true
|
|
21
|
+
});
|
|
22
|
+
logMessage(
|
|
23
|
+
`${EMOJI.cardIndexDividers} Copying assets (${index + 1}/${
|
|
24
|
+
assets.length
|
|
25
|
+
})`,
|
|
26
|
+
true
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
} catch (error) {
|
|
30
|
+
logError(error);
|
|
31
|
+
} finally {
|
|
32
|
+
logMessage('\n');
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export default copyAssets;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import copyAssets from './copyAssets.mjs';
|
|
2
|
+
import preflight from './preflight.mjs';
|
|
3
|
+
import processCSS from './processCSS.mjs';
|
|
4
|
+
import processHTML from './processHTML.mjs';
|
|
5
|
+
import processJS from './processJS.mjs';
|
|
6
|
+
|
|
7
|
+
export { copyAssets, preflight, processCSS, processHTML, processJS };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { mkdir, readdir, rm } from 'fs/promises';
|
|
2
|
+
import { EMOJI, logError, logMessage } from '../helpers/index.mjs';
|
|
3
|
+
|
|
4
|
+
const preflight = async (output) => {
|
|
5
|
+
try {
|
|
6
|
+
const files = await readdir(output);
|
|
7
|
+
logMessage(
|
|
8
|
+
`${EMOJI.wastebasket} Removing old release files (0/${files.length})`
|
|
9
|
+
);
|
|
10
|
+
for (const [index, file] of files.entries()) {
|
|
11
|
+
logMessage(
|
|
12
|
+
`${EMOJI.wastebasket} Removing old release files (${index + 1}/${
|
|
13
|
+
files.length
|
|
14
|
+
})`,
|
|
15
|
+
true
|
|
16
|
+
);
|
|
17
|
+
await rm(`${output}/${file}`, { recursive: true, force: true });
|
|
18
|
+
}
|
|
19
|
+
} catch (error) {
|
|
20
|
+
if (error.code === 'ENOENT') {
|
|
21
|
+
logMessage(`${EMOJI.fileFolder} Creating release directory`);
|
|
22
|
+
await mkdir(output);
|
|
23
|
+
} else {
|
|
24
|
+
logError(error);
|
|
25
|
+
}
|
|
26
|
+
} finally {
|
|
27
|
+
logMessage('\n');
|
|
28
|
+
}
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export default preflight;
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import cssnanoPlugin from 'cssnano';
|
|
2
|
+
import { readFile, writeFile } from 'fs/promises';
|
|
3
|
+
import postcss from 'postcss';
|
|
4
|
+
import atImport from 'postcss-import';
|
|
5
|
+
import postcssPresetEnv from 'postcss-preset-env';
|
|
6
|
+
import url from 'postcss-url';
|
|
7
|
+
import { EMOJI, logError, logMessage, OPTIONS } from '../helpers/index.mjs';
|
|
8
|
+
|
|
9
|
+
const processCSS = async (input, output, file) => {
|
|
10
|
+
logMessage(`${EMOJI.artistPalette} Processing styles`);
|
|
11
|
+
try {
|
|
12
|
+
const data = await readFile(`${input}/${file}`, {
|
|
13
|
+
encoding: 'utf8'
|
|
14
|
+
});
|
|
15
|
+
await postcss()
|
|
16
|
+
.use(atImport())
|
|
17
|
+
.use(postcssPresetEnv())
|
|
18
|
+
.use(url(OPTIONS.postcss.url))
|
|
19
|
+
.use(cssnanoPlugin(OPTIONS.postcss.cssnano))
|
|
20
|
+
.process(data, {
|
|
21
|
+
from: `${input}/${file}`
|
|
22
|
+
})
|
|
23
|
+
.then(({ css }) => {
|
|
24
|
+
writeFile(`${output}/${file}`, css, { encoding: 'utf8' });
|
|
25
|
+
});
|
|
26
|
+
} catch (error) {
|
|
27
|
+
logError(error);
|
|
28
|
+
} finally {
|
|
29
|
+
logMessage('\n');
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export default processCSS;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { readFile, writeFile } from 'fs/promises';
|
|
2
|
+
import htmlnano from 'htmlnano';
|
|
3
|
+
import posthtml from 'posthtml';
|
|
4
|
+
import { hash } from 'posthtml-hash';
|
|
5
|
+
import { EMOJI, logError, logMessage, OPTIONS } from '../helpers/index.mjs';
|
|
6
|
+
|
|
7
|
+
const processHTML = async (input, output, file) => {
|
|
8
|
+
logMessage(`${EMOJI.fileCabinet} Processing HTML`);
|
|
9
|
+
try {
|
|
10
|
+
const data = await readFile(`${input}/${file}`, { encoding: 'utf8' });
|
|
11
|
+
await posthtml()
|
|
12
|
+
.use(htmlnano(OPTIONS.posthtml.htmlnano, htmlnano.presets.safe))
|
|
13
|
+
.use(hash({ path: output, pattern: new RegExp(/hash/) }))
|
|
14
|
+
.process(data)
|
|
15
|
+
.then(({ html }) => {
|
|
16
|
+
writeFile(`${output}/${file}`, html, { encoding: 'utf8' });
|
|
17
|
+
});
|
|
18
|
+
} catch (error) {
|
|
19
|
+
logError(error);
|
|
20
|
+
} finally {
|
|
21
|
+
logMessage('\n');
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export default processHTML;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { transformFromAstAsync } from '@babel/core';
|
|
2
|
+
import { parse } from '@babel/parser';
|
|
3
|
+
import { readFile, writeFile } from 'fs/promises';
|
|
4
|
+
import { rollup } from 'rollup';
|
|
5
|
+
import { EMOJI, logError, logMessage, OPTIONS } from '../helpers/index.mjs';
|
|
6
|
+
|
|
7
|
+
const bundleJS = async (input, output, file) => {
|
|
8
|
+
try {
|
|
9
|
+
const bundle = await rollup({
|
|
10
|
+
input: `${input}/${file}`
|
|
11
|
+
});
|
|
12
|
+
await bundle.write({
|
|
13
|
+
file: `${output}/${file}`
|
|
14
|
+
});
|
|
15
|
+
await bundle.close();
|
|
16
|
+
} catch (error) {
|
|
17
|
+
logError(error);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const processJS = async (input, output, file) => {
|
|
22
|
+
logMessage(`${EMOJI.barChart} Processing scripts`);
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
await bundleJS(input, output, file);
|
|
26
|
+
const data = await readFile(`${output}/${file}`, {
|
|
27
|
+
encoding: 'utf8'
|
|
28
|
+
});
|
|
29
|
+
const ast = parse(data, {
|
|
30
|
+
sourceType: 'module'
|
|
31
|
+
});
|
|
32
|
+
const { code } = await transformFromAstAsync(ast, data, OPTIONS.babel);
|
|
33
|
+
await writeFile(`${output}/${file}`, code, { encoding: 'utf8' });
|
|
34
|
+
} catch (error) {
|
|
35
|
+
logError(error);
|
|
36
|
+
} finally {
|
|
37
|
+
logMessage('\n');
|
|
38
|
+
}
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export default processJS;
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@zulaica/site-bundler",
|
|
3
|
+
"author": "David Zulaica",
|
|
4
|
+
"version": "0.4.3",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"engines": {
|
|
7
|
+
"node": ">=16.18.1"
|
|
8
|
+
},
|
|
9
|
+
"license": "UNLICENSED",
|
|
10
|
+
"bin": {
|
|
11
|
+
"site-bundler": "lib/index.mjs"
|
|
12
|
+
},
|
|
13
|
+
"scripts": {
|
|
14
|
+
"lint": "eslint \"lib/**\"",
|
|
15
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
16
|
+
},
|
|
17
|
+
"repository": {
|
|
18
|
+
"type": "git",
|
|
19
|
+
"url": "git+https://github.com/zulaica/personal-website-bundler.git"
|
|
20
|
+
},
|
|
21
|
+
"bugs": {
|
|
22
|
+
"url": "https://github.com/zulaica/personal-website-bundler/issues"
|
|
23
|
+
},
|
|
24
|
+
"homepage": "https://github.com/zulaica/personal-website-bundler#readme",
|
|
25
|
+
"volta": {
|
|
26
|
+
"node": "16.18.1",
|
|
27
|
+
"npm": "8.19.2"
|
|
28
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@babel/core": "7.21.0",
|
|
31
|
+
"@babel/parser": "7.21.2",
|
|
32
|
+
"@babel/preset-env": "7.20.2",
|
|
33
|
+
"commander": "10.0.0",
|
|
34
|
+
"cssnano": "5.1.15",
|
|
35
|
+
"cssnano-preset-advanced": "5.3.10",
|
|
36
|
+
"htmlnano": "2.0.3",
|
|
37
|
+
"postcss": "8.4.21",
|
|
38
|
+
"postcss-import": "15.1.0",
|
|
39
|
+
"postcss-preset-env": "8.0.1",
|
|
40
|
+
"postcss-url": "10.1.3",
|
|
41
|
+
"posthtml": "0.16.6",
|
|
42
|
+
"posthtml-hash": "1.2.2",
|
|
43
|
+
"rollup": "3.19.1"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"eslint": "8.36.0",
|
|
47
|
+
"eslint-config-prettier": "8.7.0",
|
|
48
|
+
"eslint-plugin-prettier": "4.2.1",
|
|
49
|
+
"prettier": "2.8.4"
|
|
50
|
+
},
|
|
51
|
+
"description": "",
|
|
52
|
+
"main": "index.mjs",
|
|
53
|
+
"directories": {
|
|
54
|
+
"lib": "lib"
|
|
55
|
+
}
|
|
56
|
+
}
|