@zulaica/site-bundler 0.7.0 → 0.8.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.
@@ -1,92 +1,93 @@
1
1
  /* node:coverage disable */
2
2
  export const EMOJI = Object.freeze({
3
- artistPalette: '\u{1f3a8}',
4
- barChart: '\u{1f4ca}',
5
- cardIndexDividers: '\u{1f5c2}',
6
- constructionWorker: '\u{1f477}',
7
- fileCabinet: '\u{1f5c4} ',
8
- fileFolder: '\u{1f4c1}',
9
- noEntry: '\u{26D4}',
10
- package: '\u{1f4e6}',
11
- wastebasket: '\u{1f5d1} '
3
+ artistPalette: "\u{1f3a8}",
4
+ barChart: "\u{1f4ca}",
5
+ cardIndexDividers: "\u{1f5c2}",
6
+ constructionWorker: "\u{1f477}",
7
+ fileCabinet: "\u{1f5c4} ",
8
+ fileFolder: "\u{1f4c1}",
9
+ noEntry: "\u{26D4}",
10
+ package: "\u{1f4e6}",
11
+ wastebasket: "\u{1f5d1} ",
12
12
  });
13
13
 
14
14
  export const EXCLUSIONS = Object.freeze([
15
- 'index.html',
16
- 'script.hash.js',
17
- 'scripts',
18
- 'style.hash.css',
19
- 'styles'
15
+ "index.html",
16
+ "script.hash.js",
17
+ "scripts",
18
+ "style.hash.css",
19
+ "styles",
20
20
  ]);
21
21
 
22
22
  export const OPTIONS = Object.freeze({
23
23
  babel: {
24
24
  presets: [
25
25
  [
26
- '@babel/preset-env',
26
+ "@babel/preset-env",
27
27
  {
28
- targets: 'defaults',
29
- modules: false
30
- }
31
- ]
28
+ targets: "defaults",
29
+ modules: false,
30
+ },
31
+ ],
32
32
  ],
33
33
  babelrc: false,
34
34
  comments: false,
35
35
  configFile: false,
36
- minified: true
36
+ minified: true,
37
37
  },
38
38
  rollup: {
39
39
  onwarn(warning, warn) {
40
- if (warning.code === 'EMPTY_BUNDLE') return;
40
+ if (warning.code === "EMPTY_BUNDLE") return;
41
41
  warn(warning);
42
- }
42
+ },
43
43
  },
44
44
  postcss: {
45
45
  cssnano: {
46
46
  preset: [
47
- 'advanced',
47
+ "advanced",
48
48
  {
49
49
  minifyFontValues: false,
50
50
  normalizeUrl: false,
51
51
  normalizeString: {
52
- preferredQuote: 'single'
53
- }
54
- }
55
- ]
52
+ preferredQuote: "single",
53
+ },
54
+ },
55
+ ],
56
56
  },
57
57
  url: [
58
58
  {
59
- filter: '**/*.woff2',
60
- url: 'rebase'
59
+ filter: "**/*.woff2",
60
+ url: "rebase",
61
61
  },
62
62
  {
63
- filter: '**/*.woff',
64
- url: 'rebase'
63
+ filter: "**/*.woff",
64
+ url: "rebase",
65
65
  },
66
66
  {
67
- filter: '**/*.ttf',
68
- url: 'rebase'
67
+ filter: "**/*.ttf",
68
+ url: "rebase",
69
69
  },
70
70
  {
71
- filter: '**/*.webp',
72
- url: (asset) => asset.relativePath
71
+ filter: "**/*.webp",
72
+ url: (asset) => asset.relativePath,
73
73
  },
74
74
  {
75
- filter: '**/*.jpg',
76
- url: (asset) => asset.relativePath
77
- }
78
- ]
75
+ filter: "**/*.jpg",
76
+ url: (asset) => asset.relativePath,
77
+ },
78
+ ],
79
79
  },
80
80
  posthtml: {
81
81
  htmlnano: {
82
82
  collapseBooleanAttributes: {
83
- amphtml: false
83
+ amphtml: false,
84
84
  },
85
- collapseWhitespace: 'aggressive',
85
+ collapseWhitespace: "aggressive",
86
+ minifyCss: false,
86
87
  minifyJs: false,
87
- removeComments: 'all',
88
- removeEmptyAttributes: false
89
- }
90
- }
88
+ removeComments: "all",
89
+ removeEmptyAttributes: false,
90
+ },
91
+ },
91
92
  });
92
93
  /* node:coverage enable */
@@ -1,17 +1,18 @@
1
- import { exit, stderr, stdout } from 'node:process';
2
- import { clearLine, cursorTo } from 'node:readline';
3
- import { EMOJI } from './constants.mjs';
1
+ import { exit, stderr, stdout } from "node:process";
2
+ import { clearLine, cursorTo } from "node:readline";
3
+ import { EMOJI } from "./constants.mjs";
4
4
 
5
5
  export default class Logger {
6
6
  static error = (error) => {
7
7
  stderr.write(`${EMOJI.noEntry} An error has occurred\n`);
8
8
  stderr.write(`${error.toString()}\n\n`);
9
- stderr.write('', () => exit(1));
9
+ stderr.write("", () => exit(1));
10
10
  };
11
11
 
12
12
  static message = (message, update = false) => {
13
- if (process.env.NODE_ENV !== 'test') {
13
+ if (process.env.NODE_ENV !== "test") {
14
14
  if (update) {
15
+ message += "\n";
15
16
  cursorTo(stdout, 0);
16
17
  clearLine(stdout, 0);
17
18
  }
@@ -1,12 +1,12 @@
1
- import { mkdir, rm } from 'node:fs/promises';
2
- import { tmpdir } from 'node:os';
3
- import { join } from 'node:path';
4
- import { afterEach, beforeEach } from 'node:test';
1
+ import { mkdir, rm } from "node:fs/promises";
2
+ import { tmpdir } from "node:os";
3
+ import { join } from "node:path";
4
+ import { afterEach, beforeEach } from "node:test";
5
5
 
6
6
  const baseDir = join(tmpdir(), `site-bundler-test-${Date.now()}`);
7
7
 
8
- export const inputDir = join(baseDir, 'input');
9
- export const outputDir = join(baseDir, 'output');
8
+ export const inputDir = join(baseDir, "input");
9
+ export const outputDir = join(baseDir, "output");
10
10
 
11
11
  export const inputCSS = `
12
12
  /**
@@ -25,7 +25,7 @@ h1 {
25
25
  `;
26
26
 
27
27
  export const processedCSS =
28
- 'body{font-family:Arial,sans-serif;margin:0;padding:20px}h1{color:#333}';
28
+ "body{font-family:Arial,sans-serif;margin:0;padding:20px}h1{color:#333}";
29
29
 
30
30
  export const inputHTML = `
31
31
  <!DOCTYPE html>
@@ -33,7 +33,16 @@ export const inputHTML = `
33
33
  <head>
34
34
  <!-- This is a comment -->
35
35
  <meta charset="utf-8" />
36
+ <style>
37
+ body {
38
+ margin: 0;
39
+ padding: 20px;
40
+ }
41
+ </style>
36
42
  <link rel="stylesheet" href="style.hash.css" />
43
+ <script>
44
+ document.documentElement.classList.add("js");
45
+ </script>
37
46
  <script src="script.hash.js"></script>
38
47
  </head>
39
48
  <body>
@@ -66,7 +75,7 @@ export const inputHTML = `
66
75
  `;
67
76
 
68
77
  export const processedHTML =
69
- '<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><link rel="stylesheet" href="style.hash.css"><script src="script.hash.js"></script></head><body><header><h1>Test Site</h1><nav><ul><li><a href="#home">Home</a></li><li><a href="#about">About</a></li></ul></nav></header><main><section><article><h2>Article Title</h2><p>Article content goes here.</p><p aria-hidden="true">This is hidden.</p></article></section><aside><input type="text" disabled placeholder="Enter text"></aside></main><footer><p>&copy; Year</p></footer></body></html>';
78
+ '<!DOCTYPE html><html lang="en"><head><meta charset="utf-8"><style>body{margin:0;padding:20px}</style><link rel="stylesheet" href="style.hash.css"><script>document.documentElement.classList.add("js");</script><script src="script.hash.js"></script></head><body><header><h1>Test Site</h1><nav><ul><li><a href="#home">Home</a></li><li><a href="#about">About</a></li></ul></nav></header><main><section><article><h2>Article Title</h2><p>Article content goes here.</p><p aria-hidden="true">This is hidden.</p></article></section><aside><input type="text" disabled placeholder="Enter text"></aside></main><footer><p Year</p></footer></body></html>';
70
79
 
71
80
  export const inputJS = `
72
81
  // This is a comment
package/lib/index.mjs CHANGED
@@ -1,24 +1,24 @@
1
1
  #!/usr/bin/env node
2
2
 
3
- import { program } from 'commander';
4
- import { access, readFile } from 'node:fs/promises';
5
- import { EMOJI } from './helpers/constants.mjs';
6
- import Logger from './helpers/logger.mjs';
3
+ import { program } from "commander";
4
+ import { access, readFile } from "node:fs/promises";
5
+ import { EMOJI } from "./helpers/constants.mjs";
6
+ import Logger from "./helpers/logger.mjs";
7
7
  import {
8
8
  copyAssets,
9
9
  preflight,
10
10
  processCSS,
11
11
  processHTML,
12
- processJS
13
- } from './modules/index.mjs';
12
+ processJS,
13
+ } from "./modules/index.mjs";
14
14
 
15
15
  const { version } = JSON.parse(
16
- await readFile(new URL('../package.json', import.meta.url))
16
+ await readFile(new URL("../package.json", import.meta.url)),
17
17
  );
18
18
  program
19
19
  .version(version)
20
- .requiredOption('-i, --input-dir <input>', 'The input directory')
21
- .requiredOption('-o, --output-dir <output>', 'The output directory')
20
+ .requiredOption("-i, --input-dir <input>", "The input directory")
21
+ .requiredOption("-o, --output-dir <output>", "The output directory")
22
22
  .parse();
23
23
  const opts = program.opts();
24
24
 
@@ -1,89 +1,89 @@
1
- import assert from 'node:assert/strict';
2
- import { execFile } from 'node:child_process';
3
- import { readdir, readFile, writeFile } from 'node:fs/promises';
4
- import { join } from 'node:path';
5
- import { describe, it } from 'node:test';
6
- import { promisify } from 'node:util';
7
- import { EMOJI } from './helpers/constants.mjs';
1
+ import assert from "node:assert/strict";
2
+ import { execFile } from "node:child_process";
3
+ import { readdir, readFile, writeFile } from "node:fs/promises";
4
+ import { join } from "node:path";
5
+ import { describe, it } from "node:test";
6
+ import { promisify } from "node:util";
7
+ import { EMOJI } from "./helpers/constants.mjs";
8
8
  import {
9
9
  inputCSS,
10
10
  inputDir,
11
11
  inputHTML,
12
12
  inputJS,
13
13
  outputDir,
14
- setUpTestHooks
15
- } from './helpers/tests.mjs';
14
+ setUpTestHooks,
15
+ } from "./helpers/tests.mjs";
16
16
 
17
17
  const execFileAsync = promisify(execFile);
18
18
  const { version } = JSON.parse(
19
- await readFile(new URL('../package.json', import.meta.url), 'utf8')
19
+ await readFile(new URL("../package.json", import.meta.url), "utf8"),
20
20
  );
21
21
 
22
22
  describe(`${EMOJI.package} CLI Integration`, () => {
23
23
  setUpTestHooks();
24
24
 
25
- it('should display version', async () => {
26
- const { stdout } = await execFileAsync('node', [
27
- join(process.cwd(), 'lib', 'index.mjs'),
28
- '--version'
25
+ it("should display version", async () => {
26
+ const { stdout } = await execFileAsync("node", [
27
+ join(process.cwd(), "lib", "index.mjs"),
28
+ "--version",
29
29
  ]);
30
30
 
31
31
  assert.ok(stdout.includes(version));
32
32
  });
33
33
 
34
- it('should process complete site', async () => {
35
- await writeFile(join(inputDir, 'index.html'), inputHTML);
36
- await writeFile(join(inputDir, 'style.hash.css'), inputCSS);
37
- await writeFile(join(inputDir, 'script.hash.js'), inputJS);
38
- await writeFile(join(inputDir, 'favicon.ico'), '');
34
+ it("should process complete site", async () => {
35
+ await writeFile(join(inputDir, "index.html"), inputHTML);
36
+ await writeFile(join(inputDir, "style.hash.css"), inputCSS);
37
+ await writeFile(join(inputDir, "script.hash.js"), inputJS);
38
+ await writeFile(join(inputDir, "favicon.ico"), "");
39
39
 
40
40
  await assert.doesNotReject(() =>
41
- execFileAsync('node', [
42
- join(process.cwd(), 'lib', 'index.mjs'),
43
- '-i',
41
+ execFileAsync("node", [
42
+ join(process.cwd(), "lib", "index.mjs"),
43
+ "-i",
44
44
  inputDir,
45
- '-o',
46
- outputDir
47
- ])
45
+ "-o",
46
+ outputDir,
47
+ ]),
48
48
  );
49
49
 
50
50
  const outputFiles = await readdir(outputDir);
51
51
 
52
52
  assert.deepEqual(outputFiles.sort(), [
53
- 'favicon.ico',
54
- 'index.html',
55
- 'script.hash.js',
56
- 'style.hash.css'
53
+ "favicon.ico",
54
+ "index.html",
55
+ "script.hash.js",
56
+ "style.hash.css",
57
57
  ]);
58
58
  });
59
59
 
60
- it('should handle non-existent input directory', async () => {
61
- const nonExistentDir = join(inputDir, 'non-existent');
60
+ it("should handle non-existent input directory", async () => {
61
+ const nonExistentDir = join(inputDir, "non-existent");
62
62
 
63
63
  await assert.rejects(
64
64
  () =>
65
- execFileAsync('node', [
66
- join(process.cwd(), 'lib', 'index.mjs'),
67
- '-i',
65
+ execFileAsync("node", [
66
+ join(process.cwd(), "lib", "index.mjs"),
67
+ "-i",
68
68
  nonExistentDir,
69
- '-o',
70
- outputDir
69
+ "-o",
70
+ outputDir,
71
71
  ]),
72
- /Input directory does not exist/
72
+ /Input directory does not exist/,
73
73
  );
74
74
  });
75
75
 
76
- it('should handle empty input directory', async () => {
76
+ it("should handle empty input directory", async () => {
77
77
  await assert.rejects(
78
78
  () =>
79
- execFileAsync('node', [
80
- join(process.cwd(), 'lib', 'index.mjs'),
81
- '-i',
79
+ execFileAsync("node", [
80
+ join(process.cwd(), "lib", "index.mjs"),
81
+ "-i",
82
82
  inputDir,
83
- '-o',
84
- outputDir
83
+ "-o",
84
+ outputDir,
85
85
  ]),
86
- /An error has occurred/
86
+ /An error has occurred/,
87
87
  );
88
88
  });
89
89
  });
@@ -1,7 +1,7 @@
1
- import { cp, readdir } from 'node:fs/promises';
2
- import { join } from 'node:path';
3
- import { EMOJI, EXCLUSIONS } from '../helpers/constants.mjs';
4
- import Logger from '../helpers/logger.mjs';
1
+ import { cp, readdir } from "node:fs/promises";
2
+ import { join } from "node:path";
3
+ import { EMOJI, EXCLUSIONS } from "../helpers/constants.mjs";
4
+ import Logger from "../helpers/logger.mjs";
5
5
 
6
6
  const copyAssets = async ({ inputDir, outputDir }) => {
7
7
  const assets = await readdir(inputDir);
@@ -12,20 +12,20 @@ const copyAssets = async ({ inputDir, outputDir }) => {
12
12
  }
13
13
 
14
14
  Logger.message(
15
- `${EMOJI.cardIndexDividers} Copying ${filteredAssets.length} assets…`
15
+ `${EMOJI.cardIndexDividers} Copying ${filteredAssets.length} assets…`,
16
16
  );
17
17
 
18
18
  await Promise.all(
19
19
  filteredAssets.map(async (asset) => {
20
20
  await cp(join(inputDir, asset), join(outputDir, asset), {
21
- recursive: true
21
+ recursive: true,
22
22
  });
23
- })
23
+ }),
24
24
  );
25
25
 
26
26
  Logger.message(
27
- `${EMOJI.cardIndexDividers} Copied ${filteredAssets.length} assets\n`,
28
- true
27
+ `${EMOJI.cardIndexDividers} Copied ${filteredAssets.length} assets`,
28
+ true,
29
29
  );
30
30
  };
31
31
 
@@ -1,55 +1,55 @@
1
- import assert from 'node:assert/strict';
2
- import { mkdir, readdir, writeFile } from 'node:fs/promises';
3
- import { join } from 'node:path';
4
- import { describe, it } from 'node:test';
5
- import { EMOJI } from '../helpers/constants.mjs';
1
+ import assert from "node:assert/strict";
2
+ import { mkdir, readdir, writeFile } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { describe, it } from "node:test";
5
+ import { EMOJI } from "../helpers/constants.mjs";
6
6
  import {
7
7
  inputCSS,
8
8
  inputDir,
9
9
  inputHTML,
10
10
  inputJS,
11
11
  outputDir,
12
- setUpTestHooks
13
- } from '../helpers/tests.mjs';
14
- import copyAssets from './copyAssets.mjs';
12
+ setUpTestHooks,
13
+ } from "../helpers/tests.mjs";
14
+ import copyAssets from "./copyAssets.mjs";
15
15
 
16
16
  describe(`${EMOJI.cardIndexDividers} copyAssets()`, () => {
17
17
  setUpTestHooks();
18
18
 
19
- it('should copy all non-excluded assets', async () => {
20
- await writeFile(join(inputDir, 'image.png'), '');
21
- await writeFile(join(inputDir, 'document.pdf'), '');
22
- await writeFile(join(inputDir, 'script.js'), inputJS);
19
+ it("should copy all non-excluded assets", async () => {
20
+ await writeFile(join(inputDir, "image.png"), "");
21
+ await writeFile(join(inputDir, "document.pdf"), "");
22
+ await writeFile(join(inputDir, "script.js"), inputJS);
23
23
 
24
24
  await assert.doesNotReject(() => copyAssets({ inputDir, outputDir }));
25
25
 
26
26
  const copiedFiles = await readdir(outputDir);
27
27
 
28
28
  assert.deepEqual(copiedFiles.sort(), [
29
- 'document.pdf',
30
- 'image.png',
31
- 'script.js'
29
+ "document.pdf",
30
+ "image.png",
31
+ "script.js",
32
32
  ]);
33
33
  });
34
34
 
35
- it('should exclude files listed in EXCLUSIONS', async () => {
36
- await mkdir(join(inputDir, 'scripts'), { recursive: true });
37
- await mkdir(join(inputDir, 'styles'), { recursive: true });
38
- await writeFile(join(inputDir, 'image.png'), '');
39
- await writeFile(join(inputDir, 'index.html'), inputHTML);
40
- await writeFile(join(inputDir, 'script.hash.js'), inputJS);
41
- await writeFile(join(inputDir, 'style.hash.css'), inputCSS);
42
- await writeFile(join(inputDir, 'scripts', 'app.js'), inputJS);
43
- await writeFile(join(inputDir, 'styles', 'main.css'), inputCSS);
35
+ it("should exclude files listed in EXCLUSIONS", async () => {
36
+ await mkdir(join(inputDir, "scripts"), { recursive: true });
37
+ await mkdir(join(inputDir, "styles"), { recursive: true });
38
+ await writeFile(join(inputDir, "image.png"), "");
39
+ await writeFile(join(inputDir, "index.html"), inputHTML);
40
+ await writeFile(join(inputDir, "script.hash.js"), inputJS);
41
+ await writeFile(join(inputDir, "style.hash.css"), inputCSS);
42
+ await writeFile(join(inputDir, "scripts", "app.js"), inputJS);
43
+ await writeFile(join(inputDir, "styles", "main.css"), inputCSS);
44
44
 
45
45
  await assert.doesNotReject(() => copyAssets({ inputDir, outputDir }));
46
46
 
47
47
  const copiedFiles = await readdir(outputDir);
48
48
 
49
- assert.deepStrictEqual(copiedFiles, ['image.png']);
49
+ assert.deepStrictEqual(copiedFiles, ["image.png"]);
50
50
  });
51
51
 
52
- it('should handle empty input directory', async () => {
52
+ it("should handle empty input directory", async () => {
53
53
  await assert.doesNotReject(() => copyAssets({ inputDir, outputDir }));
54
54
 
55
55
  const copiedFiles = await readdir(outputDir);
@@ -57,25 +57,25 @@ describe(`${EMOJI.cardIndexDividers} copyAssets()`, () => {
57
57
  assert.deepStrictEqual(copiedFiles, []);
58
58
  });
59
59
 
60
- it('should copy directories recursively', async () => {
61
- await mkdir(join(inputDir, 'assets'), { recursive: true });
62
- await mkdir(join(inputDir, 'assets', 'images'), { recursive: true });
63
- await writeFile(join(inputDir, 'assets', 'file.txt'), '');
64
- await writeFile(join(inputDir, 'assets', 'images', 'logo.png'), '');
60
+ it("should copy directories recursively", async () => {
61
+ await mkdir(join(inputDir, "assets"), { recursive: true });
62
+ await mkdir(join(inputDir, "assets", "images"), { recursive: true });
63
+ await writeFile(join(inputDir, "assets", "file.txt"), "");
64
+ await writeFile(join(inputDir, "assets", "images", "logo.png"), "");
65
65
 
66
66
  await assert.doesNotReject(() => copyAssets({ inputDir, outputDir }));
67
67
 
68
68
  const copiedFiles = await readdir(outputDir, { withFileTypes: true });
69
- const assetsDir = copiedFiles.find((f) => f.name === 'assets');
70
- const subFiles = await readdir(join(outputDir, 'assets'));
69
+ const assetsDir = copiedFiles.find((f) => f.name === "assets");
70
+ const subFiles = await readdir(join(outputDir, "assets"));
71
71
 
72
72
  assert.ok(assetsDir.isDirectory());
73
- assert.deepStrictEqual(subFiles.sort(), ['file.txt', 'images']);
73
+ assert.deepStrictEqual(subFiles.sort(), ["file.txt", "images"]);
74
74
  });
75
75
 
76
- it('should handle only excluded files', async () => {
77
- await writeFile(join(inputDir, 'index.html'), inputHTML);
78
- await writeFile(join(inputDir, 'script.hash.js'), inputCSS);
76
+ it("should handle only excluded files", async () => {
77
+ await writeFile(join(inputDir, "index.html"), inputHTML);
78
+ await writeFile(join(inputDir, "script.hash.js"), inputCSS);
79
79
 
80
80
  await assert.doesNotReject(() => copyAssets({ inputDir, outputDir }));
81
81
 
@@ -1,7 +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';
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
6
 
7
7
  export { copyAssets, preflight, processCSS, processHTML, processJS };
@@ -1,6 +1,6 @@
1
- import { mkdir, readdir, rm } from 'node:fs/promises';
2
- import { EMOJI } from '../helpers/constants.mjs';
3
- import Logger from '../helpers/logger.mjs';
1
+ import { mkdir, readdir, rm } from "node:fs/promises";
2
+ import { EMOJI } from "../helpers/constants.mjs";
3
+ import Logger from "../helpers/logger.mjs";
4
4
 
5
5
  const preflight = async (outputDir) => {
6
6
  try {
@@ -13,22 +13,22 @@ const preflight = async (outputDir) => {
13
13
  await mkdir(outputDir);
14
14
 
15
15
  Logger.message(
16
- `${EMOJI.wastebasket} Removed old ${outputDir} files\n`,
17
- true
16
+ `${EMOJI.wastebasket} Removed old ${outputDir} files`,
17
+ true,
18
18
  );
19
19
  }
20
20
  } catch (error) {
21
- if (error.code === 'ENOENT') {
21
+ if (error.code === "ENOENT") {
22
22
  Logger.message(`${EMOJI.fileFolder} Creating ${outputDir} directory…`);
23
23
 
24
24
  await mkdir(outputDir);
25
25
 
26
26
  Logger.message(
27
- `${EMOJI.fileFolder} Created ${outputDir} directory\n`,
28
- true
27
+ `${EMOJI.fileFolder} Created ${outputDir} directory`,
28
+ true,
29
29
  );
30
30
  } else {
31
- Logger.error(`${error} \n`);
31
+ Logger.error(error);
32
32
  }
33
33
  }
34
34
  };
@@ -1,16 +1,16 @@
1
- import assert from 'node:assert/strict';
2
- import { mkdir, readdir, writeFile } from 'node:fs/promises';
3
- import { join } from 'node:path';
4
- import { describe, it } from 'node:test';
5
- import { EMOJI } from '../helpers/constants.mjs';
6
- import { outputDir, setUpTestHooks } from '../helpers/tests.mjs';
7
- import preflight from './preflight.mjs';
1
+ import assert from "node:assert/strict";
2
+ import { mkdir, readdir, writeFile } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { describe, it } from "node:test";
5
+ import { EMOJI } from "../helpers/constants.mjs";
6
+ import { outputDir, setUpTestHooks } from "../helpers/tests.mjs";
7
+ import preflight from "./preflight.mjs";
8
8
 
9
9
  describe(`${EMOJI.fileFolder} preflight()`, () => {
10
10
  setUpTestHooks();
11
11
 
12
- it('should create output directory if it does not exist', async () => {
13
- const nonExistentDir = join(outputDir, 'non-existent');
12
+ it("should create output directory if it does not exist", async () => {
13
+ const nonExistentDir = join(outputDir, "non-existent");
14
14
  await assert.doesNotReject(() => preflight(nonExistentDir));
15
15
 
16
16
  const files = await readdir(nonExistentDir);
@@ -19,11 +19,11 @@ describe(`${EMOJI.fileFolder} preflight()`, () => {
19
19
  assert.deepEqual(files, []);
20
20
  });
21
21
 
22
- it('should remove all files and recreate output directory', async () => {
23
- await mkdir(join(outputDir, 'subdir'), { recursive: true });
24
- await writeFile(join(outputDir, 'file1.txt'), '');
25
- await writeFile(join(outputDir, 'file2.txt'), '');
26
- await writeFile(join(outputDir, 'subdir', 'file3.txt'), '');
22
+ it("should remove all files and recreate output directory", async () => {
23
+ await mkdir(join(outputDir, "subdir"), { recursive: true });
24
+ await writeFile(join(outputDir, "file1.txt"), "");
25
+ await writeFile(join(outputDir, "file2.txt"), "");
26
+ await writeFile(join(outputDir, "subdir", "file3.txt"), "");
27
27
 
28
28
  const initialFiles = await readdir(outputDir, { withFileTypes: true });
29
29
 
@@ -1,12 +1,12 @@
1
- import cssnanoPlugin from 'cssnano';
2
- import { readdir, readFile, writeFile } from 'node:fs/promises';
3
- import { join } from 'node:path';
4
- import postcss from 'postcss';
5
- import atImport from 'postcss-import';
6
- import postcssPresetEnv from 'postcss-preset-env';
7
- import url from 'postcss-url';
8
- import { EMOJI, OPTIONS } from '../helpers/constants.mjs';
9
- import Logger from '../helpers/logger.mjs';
1
+ import cssnanoPlugin from "cssnano";
2
+ import { readdir, readFile, writeFile } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import postcss from "postcss";
5
+ import atImport from "postcss-import";
6
+ import postcssPresetEnv from "postcss-preset-env";
7
+ import url from "postcss-url";
8
+ import { EMOJI, OPTIONS } from "../helpers/constants.mjs";
9
+ import Logger from "../helpers/logger.mjs";
10
10
 
11
11
  const { cssnano, url: cssUrl } = OPTIONS.postcss;
12
12
 
@@ -18,7 +18,7 @@ const postcssProcessor = postcss()
18
18
 
19
19
  const processCSS = async ({ inputDir, outputDir }) => {
20
20
  const assets = await readdir(inputDir);
21
- const file = 'style.hash.css';
21
+ const file = "style.hash.css";
22
22
 
23
23
  if (!assets.includes(file)) {
24
24
  return;
@@ -28,15 +28,15 @@ const processCSS = async ({ inputDir, outputDir }) => {
28
28
 
29
29
  const input = join(inputDir, file);
30
30
  const output = join(outputDir, file);
31
- const data = await readFile(input, { encoding: 'utf8' });
32
- const code = await _processData(input, data);
31
+ const data = await readFile(input, { encoding: "utf8" });
32
+ const code = await processCSSData(input, data);
33
33
 
34
34
  await writeFile(output, code);
35
35
 
36
- Logger.message(`${EMOJI.artistPalette} Processed styles\n`, true);
36
+ Logger.message(`${EMOJI.artistPalette} Processed styles`, true);
37
37
  };
38
38
 
39
- async function _processData(input, data) {
39
+ export async function processCSSData(input, data) {
40
40
  const { css } = await postcssProcessor.process(data, { from: input });
41
41
 
42
42
  return css;
@@ -1,60 +1,60 @@
1
- import assert from 'node:assert/strict';
2
- import { readFile, writeFile } from 'node:fs/promises';
3
- import { join } from 'node:path';
4
- import { describe, it } from 'node:test';
5
- import { EMOJI } from '../helpers/constants.mjs';
1
+ import assert from "node:assert/strict";
2
+ import { readFile, writeFile } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { describe, it } from "node:test";
5
+ import { EMOJI } from "../helpers/constants.mjs";
6
6
  import {
7
7
  inputCSS,
8
8
  inputDir,
9
9
  outputDir,
10
10
  processedCSS,
11
- setUpTestHooks
12
- } from '../helpers/tests.mjs';
13
- import processCSS from './processCSS.mjs';
11
+ setUpTestHooks,
12
+ } from "../helpers/tests.mjs";
13
+ import processCSS from "./processCSS.mjs";
14
14
 
15
15
  describe(`${EMOJI.artistPalette} processCSS()`, () => {
16
16
  setUpTestHooks();
17
17
 
18
- it('should process CSS file and write output', async (sub) => {
19
- await writeFile(join(inputDir, 'style.hash.css'), inputCSS);
18
+ it("should process CSS file and write output", async (sub) => {
19
+ await writeFile(join(inputDir, "style.hash.css"), inputCSS);
20
20
 
21
21
  await assert.doesNotReject(() => processCSS({ inputDir, outputDir }));
22
22
 
23
- const output = await readFile(join(outputDir, 'style.hash.css'), 'utf8');
23
+ const output = await readFile(join(outputDir, "style.hash.css"), "utf8");
24
24
 
25
- sub.test('minimized as a single line', () => {
25
+ sub.test("minimized as a single line", () => {
26
26
  assert.equal(output, processedCSS);
27
27
  });
28
- sub.test('with whitespace trimmed', () => {
28
+ sub.test("with whitespace trimmed", () => {
29
29
  assert.ok(output.trim().length < inputCSS.trim().length);
30
30
  });
31
- sub.test('with comments removed', () => {
31
+ sub.test("with comments removed", () => {
32
32
  assert.doesNotMatch(output, /\/\*\*/);
33
33
  });
34
34
  });
35
35
 
36
- it('should handle CSS imports', async () => {
37
- await writeFile(join(inputDir, 'style.hash.css'), '@import "base.css";');
38
- await writeFile(join(inputDir, 'base.css'), inputCSS);
36
+ it("should handle CSS imports", async () => {
37
+ await writeFile(join(inputDir, "style.hash.css"), '@import "base.css";');
38
+ await writeFile(join(inputDir, "base.css"), inputCSS);
39
39
 
40
40
  await assert.doesNotReject(() => processCSS({ inputDir, outputDir }));
41
41
 
42
- const output = await readFile(join(outputDir, 'style.hash.css'), 'utf8');
42
+ const output = await readFile(join(outputDir, "style.hash.css"), "utf8");
43
43
 
44
44
  assert.ok(output.includes(processedCSS));
45
45
  });
46
46
 
47
- it('should handle empty CSS file', async () => {
48
- await writeFile(join(inputDir, 'style.hash.css'), '');
47
+ it("should handle empty CSS file", async () => {
48
+ await writeFile(join(inputDir, "style.hash.css"), "");
49
49
 
50
50
  await assert.doesNotReject(() => processCSS({ inputDir, outputDir }));
51
51
 
52
- const output = await readFile(join(outputDir, 'style.hash.css'), 'utf8');
52
+ const output = await readFile(join(outputDir, "style.hash.css"), "utf8");
53
53
 
54
- assert.equal(output, '');
54
+ assert.equal(output, "");
55
55
  });
56
56
 
57
- it('should skip processing if CSS file does not exist', async () => {
57
+ it("should skip processing if CSS file does not exist", async () => {
58
58
  await assert.doesNotReject(() => processCSS({ inputDir, outputDir }));
59
59
  });
60
60
  });
@@ -1,37 +1,40 @@
1
- import htmlnano from 'htmlnano';
2
- import { readFile, writeFile } from 'node:fs/promises';
3
- import { join } from 'node:path';
4
- import posthtml from 'posthtml';
5
- import { hash } from 'posthtml-hash';
6
- import { EMOJI, OPTIONS } from '../helpers/constants.mjs';
7
- import Logger from '../helpers/logger.mjs';
1
+ import htmlnano from "htmlnano";
2
+ import { readFile, writeFile } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import posthtml from "posthtml";
5
+ import { hash } from "posthtml-hash";
6
+ import { EMOJI, OPTIONS } from "../helpers/constants.mjs";
7
+ import Logger from "../helpers/logger.mjs";
8
+ import { processCSSData } from "./processCSS.mjs";
9
+ import { processJSData } from "./processJS.mjs";
8
10
 
9
11
  const { htmlnano: htmlnanoOptions } = OPTIONS.posthtml;
10
12
 
11
13
  const processHTML = async ({ inputDir, outputDir }) => {
12
14
  Logger.message(`${EMOJI.fileCabinet} Processing HTML…`);
13
- const file = 'index.html';
15
+ const file = "index.html";
14
16
  const input = join(inputDir, file);
15
17
  const output = join(outputDir, file);
16
18
 
17
- const data = await readFile(input, { encoding: 'utf8' });
18
- const code = await _processData(outputDir, data);
19
+ const data = await readFile(input, { encoding: "utf8" });
20
+ const code = await processHTMLData(outputDir, data);
19
21
 
20
22
  await writeFile(output, code);
21
23
 
22
- Logger.message(`${EMOJI.fileCabinet} Processed HTML\n`, true);
24
+ Logger.message(`${EMOJI.fileCabinet} Processed HTML`, true);
23
25
  };
24
26
 
25
- async function _processData(path, data) {
26
- let posthtmlInstance = posthtml().use(
27
- htmlnano(htmlnanoOptions, htmlnano.presets.safe)
28
- );
27
+ async function processHTMLData(path, data) {
28
+ let posthtmlInstance = posthtml()
29
+ .use(processInlineStyles())
30
+ .use(processInlineScripts())
31
+ .use(htmlnano(htmlnanoOptions, htmlnano.presets.safe));
29
32
 
30
33
  /* node:coverage disable */
31
- // Skip hash replacement during tests
32
- if (process.env.NODE_ENV !== 'test') {
34
+ // Do not perform hash replacement when running tests
35
+ if (process.env.NODE_ENV !== "test") {
33
36
  posthtmlInstance = posthtmlInstance.use(
34
- hash({ path, pattern: new RegExp(/hash/) })
37
+ hash({ path, pattern: new RegExp(/hash/) }),
35
38
  );
36
39
  }
37
40
  /* node:coverage enable */
@@ -41,4 +44,48 @@ async function _processData(path, data) {
41
44
  return html;
42
45
  }
43
46
 
47
+ /* node:coverage disable */
48
+ function processInlineContent(tagName, shouldProcess, processFn) {
49
+ return function () {
50
+ return async function (tree) {
51
+ const itemsToProcess = [];
52
+
53
+ tree.walk((node) => {
54
+ if (node.tag === tagName && shouldProcess(node)) {
55
+ const content = Array.isArray(node.content)
56
+ ? node.content.join("")
57
+ : node.content;
58
+
59
+ itemsToProcess.push({ node, content });
60
+ }
61
+
62
+ return node;
63
+ });
64
+
65
+ await Promise.all(
66
+ itemsToProcess.map(async ({ node, content }) => {
67
+ node.content = await processFn(content);
68
+ }),
69
+ );
70
+ };
71
+ };
72
+ }
73
+ /* node:coverage enable */
74
+
75
+ /* node:coverage disable */
76
+ const processInlineScripts = processInlineContent(
77
+ "script",
78
+ (node) => node.content && !node.attrs?.src,
79
+ processJSData,
80
+ );
81
+ /* node:coverage enable */
82
+
83
+ /* node:coverage disable */
84
+ const processInlineStyles = processInlineContent(
85
+ "style",
86
+ (node) => node.content,
87
+ (content) => processCSSData(null, content),
88
+ );
89
+ /* node:coverage enable */
90
+
44
91
  export default processHTML;
@@ -1,50 +1,50 @@
1
- import assert from 'node:assert/strict';
2
- import { readFile, writeFile } from 'node:fs/promises';
3
- import { join } from 'node:path';
4
- import { describe, it } from 'node:test';
5
- import { EMOJI } from '../helpers/constants.mjs';
1
+ import assert from "node:assert/strict";
2
+ import { readFile, writeFile } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { describe, it } from "node:test";
5
+ import { EMOJI } from "../helpers/constants.mjs";
6
6
  import {
7
7
  inputDir,
8
8
  inputHTML,
9
9
  outputDir,
10
10
  processedHTML,
11
- setUpTestHooks
12
- } from '../helpers/tests.mjs';
13
- import processHTML from './processHTML.mjs';
11
+ setUpTestHooks,
12
+ } from "../helpers/tests.mjs";
13
+ import processHTML from "./processHTML.mjs";
14
14
 
15
15
  describe(`${EMOJI.fileCabinet} processHTML()`, () => {
16
16
  setUpTestHooks();
17
17
 
18
- it('should process HTML file and write output', async (sub) => {
19
- await writeFile(join(inputDir, 'index.html'), inputHTML);
18
+ it("should process HTML file and write output", async (sub) => {
19
+ await writeFile(join(inputDir, "index.html"), inputHTML);
20
20
 
21
21
  await assert.doesNotReject(() => processHTML({ inputDir, outputDir }));
22
22
 
23
- const output = await readFile(join(outputDir, 'index.html'), 'utf8');
23
+ const output = await readFile(join(outputDir, "index.html"), "utf8");
24
24
 
25
- sub.test('minimized as a single line', () => {
25
+ sub.test("minimized as a single line", () => {
26
26
  assert.equal(output, processedHTML);
27
27
  });
28
- sub.test('with whitespace trimmed', () => {
28
+ sub.test("with whitespace trimmed", () => {
29
29
  assert.ok(output.trim().length < inputHTML.trim().length);
30
- assert.ok(output.includes('Test Site'));
31
- assert.ok(output.includes('Article Title'));
30
+ assert.ok(output.includes("Test Site"));
31
+ assert.ok(output.includes("Article Title"));
32
32
  });
33
- sub.test('with comments removed', () => {
33
+ sub.test("with comments removed", () => {
34
34
  assert.doesNotMatch(output, /<!--/);
35
35
  });
36
- sub.test('with boolean attributes handled correctly', () => {
37
- assert.ok(output.includes('disabled'));
36
+ sub.test("with boolean attributes handled correctly", () => {
37
+ assert.ok(output.includes("disabled"));
38
38
  assert.doesNotMatch(output, /disabled="disabled"/);
39
39
  });
40
40
  });
41
41
 
42
- it('should handle empty HTML file', async () => {
43
- await writeFile(join(inputDir, 'index.html'), '');
42
+ it("should handle empty HTML file", async () => {
43
+ await writeFile(join(inputDir, "index.html"), "");
44
44
  await processHTML({ inputDir, outputDir });
45
45
 
46
- const output = await readFile(join(outputDir, 'index.html'), 'utf8');
46
+ const output = await readFile(join(outputDir, "index.html"), "utf8");
47
47
 
48
- assert.equal(output, '');
48
+ assert.equal(output, "");
49
49
  });
50
50
  });
@@ -1,16 +1,16 @@
1
- import { transformFromAstAsync } from '@babel/core';
2
- import { parse } from '@babel/parser';
3
- import { readdir, writeFile } from 'node:fs/promises';
4
- import { join } from 'node:path';
5
- import { rollup } from 'rollup';
6
- import { EMOJI, OPTIONS } from '../helpers/constants.mjs';
7
- import Logger from '../helpers/logger.mjs';
1
+ import { transformFromAstAsync } from "@babel/core";
2
+ import { parse } from "@babel/parser";
3
+ import { readdir, writeFile } from "node:fs/promises";
4
+ import { join } from "node:path";
5
+ import { rollup } from "rollup";
6
+ import { EMOJI, OPTIONS } from "../helpers/constants.mjs";
7
+ import Logger from "../helpers/logger.mjs";
8
8
 
9
9
  const { babel: babelOptions, rollup: rollupOptions } = OPTIONS;
10
10
 
11
11
  const processJS = async ({ inputDir, outputDir }) => {
12
12
  const assets = await readdir(inputDir);
13
- const file = 'script.hash.js';
13
+ const file = "script.hash.js";
14
14
 
15
15
  if (!assets.includes(file)) {
16
16
  return;
@@ -20,28 +20,28 @@ const processJS = async ({ inputDir, outputDir }) => {
20
20
 
21
21
  const input = join(inputDir, file);
22
22
  const output = join(outputDir, file);
23
- const bundledCode = await _bundleJS(input);
24
- const code = await _processData(bundledCode);
23
+ const bundledCode = await bundleJS(input);
24
+ const code = await processJSData(bundledCode);
25
25
 
26
26
  await writeFile(output, code);
27
27
 
28
- Logger.message(`${EMOJI.barChart} Processed scripts\n`, true);
28
+ Logger.message(`${EMOJI.barChart} Processed scripts`, true);
29
29
  };
30
30
 
31
- async function _bundleJS(input) {
31
+ async function bundleJS(input) {
32
32
  const bundle = await rollup({
33
33
  input,
34
- ...rollupOptions
34
+ ...rollupOptions,
35
35
  });
36
- const { output } = await bundle.generate({ format: 'es' });
36
+ const { output } = await bundle.generate({ format: "es" });
37
37
 
38
38
  await bundle.close();
39
39
 
40
40
  return output[0].code;
41
41
  }
42
42
 
43
- async function _processData(data) {
44
- const ast = parse(data, { sourceType: 'module' });
43
+ export async function processJSData(data) {
44
+ const ast = parse(data, { sourceType: "module" });
45
45
  const { code } = await transformFromAstAsync(ast, data, babelOptions);
46
46
 
47
47
  return code;
@@ -1,67 +1,67 @@
1
- import assert from 'node:assert/strict';
2
- import { readFile, writeFile } from 'node:fs/promises';
3
- import { join } from 'node:path';
4
- import { describe, it } from 'node:test';
5
- import { EMOJI } from '../helpers/constants.mjs';
1
+ import assert from "node:assert/strict";
2
+ import { readFile, writeFile } from "node:fs/promises";
3
+ import { join } from "node:path";
4
+ import { describe, it } from "node:test";
5
+ import { EMOJI } from "../helpers/constants.mjs";
6
6
  import {
7
7
  inputDir,
8
8
  inputJS,
9
9
  outputDir,
10
10
  processedJS,
11
- setUpTestHooks
12
- } from '../helpers/tests.mjs';
13
- import processJS from './processJS.mjs';
11
+ setUpTestHooks,
12
+ } from "../helpers/tests.mjs";
13
+ import processJS from "./processJS.mjs";
14
14
 
15
15
  describe(`${EMOJI.barChart} processJS()`, () => {
16
16
  setUpTestHooks();
17
17
 
18
- it('should process JavaScript file and write output', async (sub) => {
19
- await writeFile(join(inputDir, 'script.hash.js'), inputJS);
18
+ it("should process JavaScript file and write output", async (sub) => {
19
+ await writeFile(join(inputDir, "script.hash.js"), inputJS);
20
20
 
21
21
  await assert.doesNotReject(() => processJS({ inputDir, outputDir }));
22
22
 
23
- const output = await readFile(join(outputDir, 'script.hash.js'), 'utf8');
23
+ const output = await readFile(join(outputDir, "script.hash.js"), "utf8");
24
24
 
25
- sub.test('minimized as a single line', () => {
25
+ sub.test("minimized as a single line", () => {
26
26
  assert.equal(output, processedJS);
27
27
  });
28
- sub.test('with whitespace trimmed', () => {
28
+ sub.test("with whitespace trimmed", () => {
29
29
  assert.ok(output.trim().length < inputJS.trim().length);
30
30
  });
31
- sub.test('with comments removed', () => {
31
+ sub.test("with comments removed", () => {
32
32
  assert.doesNotMatch(output, /\/\/ This is a comment/);
33
33
  assert.doesNotMatch(output, /\/\*\*/);
34
34
  });
35
35
  });
36
36
 
37
- it('should bundle ES modules', async () => {
37
+ it("should bundle ES modules", async () => {
38
38
  await writeFile(
39
- join(inputDir, 'script.hash.js'),
39
+ join(inputDir, "script.hash.js"),
40
40
  `
41
41
  import sum from './input.js';
42
42
  console.log(sum(2, 3));
43
- `
43
+ `,
44
44
  );
45
- await writeFile(join(inputDir, 'input.js'), inputJS);
45
+ await writeFile(join(inputDir, "input.js"), inputJS);
46
46
 
47
47
  await assert.doesNotReject(() => processJS({ inputDir, outputDir }));
48
48
 
49
- const output = await readFile(join(outputDir, 'script.hash.js'), 'utf8');
49
+ const output = await readFile(join(outputDir, "script.hash.js"), "utf8");
50
50
 
51
- assert.ok(output.includes('const sum=(a,b)=>{return a+b}'));
51
+ assert.ok(output.includes("const sum=(a,b)=>{return a+b}"));
52
52
  });
53
53
 
54
- it('should handle empty JavaScript file', async () => {
55
- await writeFile(join(inputDir, 'script.hash.js'), '');
54
+ it("should handle empty JavaScript file", async () => {
55
+ await writeFile(join(inputDir, "script.hash.js"), "");
56
56
 
57
57
  await assert.doesNotReject(() => processJS({ inputDir, outputDir }));
58
58
 
59
- const output = await readFile(join(outputDir, 'script.hash.js'), 'utf8');
59
+ const output = await readFile(join(outputDir, "script.hash.js"), "utf8");
60
60
 
61
- assert.equal(output, '');
61
+ assert.equal(output, "");
62
62
  });
63
63
 
64
- it('should skip processing if JavaScript file does not exist', async () => {
64
+ it("should skip processing if JavaScript file does not exist", async () => {
65
65
  await assert.doesNotReject(() => processJS({ inputDir, outputDir }));
66
66
  });
67
67
  });
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@zulaica/site-bundler",
3
3
  "author": "David Zulaica",
4
- "version": "0.7.0",
4
+ "version": "0.8.0",
5
5
  "type": "module",
6
6
  "engines": {
7
- "node": ">=24.12.0"
7
+ "node": ">=24.18.0"
8
8
  },
9
9
  "license": "UNLICENSED",
10
10
  "bin": {
@@ -22,7 +22,7 @@
22
22
  "lint": "eslint \"lib/**\"",
23
23
  "test": "NODE_ENV=test node --test --test-concurrency=1 --require \"./lib/helpers/tests.mjs\"",
24
24
  "test:watch": "npm run test -- --watch",
25
- "test:coverage": "npm run test -- --experimental-test-coverage"
25
+ "test:coverage": "npm run test -- --experimental-test-coverage --test-coverage-exclude=\"**/helpers/**\" --test-coverage-exclude=\"**/*.test.mjs\""
26
26
  },
27
27
  "repository": {
28
28
  "type": "git",
@@ -33,29 +33,33 @@
33
33
  },
34
34
  "homepage": "https://github.com/zulaica/site-bundler#readme",
35
35
  "dependencies": {
36
- "@babel/core": "7.28.5",
37
- "@babel/parser": "7.28.5",
38
- "@babel/preset-env": "7.28.5",
39
- "commander": "14.0.2",
40
- "cssnano": "7.1.2",
41
- "cssnano-preset-advanced": "7.0.10",
42
- "htmlnano": "3.0.0",
43
- "postcss": "8.5.6",
36
+ "@babel/core": "8.0.1",
37
+ "@babel/parser": "8.0.4",
38
+ "@babel/preset-env": "8.0.2",
39
+ "commander": "15.0.0",
40
+ "cssnano": "8.0.2",
41
+ "cssnano-preset-advanced": "8.0.2",
42
+ "htmlnano": "3.4.0",
43
+ "postcss": "8.5.20",
44
44
  "postcss-import": "16.1.1",
45
- "postcss-preset-env": "10.5.0",
46
- "postcss-url": "10.1.3",
45
+ "postcss-preset-env": "11.3.2",
46
+ "postcss-url": "10.1.4",
47
47
  "posthtml": "0.16.7",
48
48
  "posthtml-hash": "1.2.2",
49
- "rollup": "4.54.0"
49
+ "rollup": "4.62.2"
50
50
  },
51
51
  "devDependencies": {
52
- "eslint": "9.39.2",
52
+ "@eslint/js": "10.0.1",
53
+ "eslint": "10.7.0",
53
54
  "eslint-config-prettier": "10.1.8",
54
- "eslint-plugin-prettier": "5.5.4",
55
- "globals": "16.5.0",
56
- "prettier": "3.7.4"
55
+ "eslint-plugin-prettier": "5.5.6",
56
+ "globals": "17.7.0",
57
+ "prettier": "3.9.5"
57
58
  },
58
59
  "volta": {
59
- "node": "24.12.0"
60
+ "node": "24.18.0"
61
+ },
62
+ "allowScripts": {
63
+ "fsevents@2.3.3": true
60
64
  }
61
65
  }