From 510cbf308e13e8f795855343d2f01b96fc162505 Mon Sep 17 00:00:00 2001 From: Tim Riley Date: Tue, 10 Oct 2023 23:37:03 +1100 Subject: [PATCH] Format everything with Prettier --- .prettierignore | 2 + .prettierrc | 1 + jest.config.ts | 13 +- src/args.ts | 4 +- src/esbuild.ts | 69 +++--- src/hanami-assets.ts | 27 ++- src/hanami-esbuild-plugin.ts | 69 +++--- test/fixtures/todo/app/assets/css/app.css | 8 +- test/fixtures/todo/app/assets/css/reset.css | 2 +- test/fixtures/todo/app/assets/js/TodoItem.tsx | 10 +- test/fixtures/todo/app/assets/js/TodoList.tsx | 12 +- test/fixtures/todo/app/assets/js/app.tsx | 10 +- .../fixtures/todo/app/assets/js/login/app.tsx | 2 +- .../todo/slices/admin/assets/js/app.js | 2 +- test/hanami-assets.test.ts | 214 +++++++++--------- 15 files changed, 225 insertions(+), 220 deletions(-) create mode 100644 .prettierignore create mode 100644 .prettierrc diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000..c5817c5 --- /dev/null +++ b/.prettierignore @@ -0,0 +1,2 @@ +# Ignore artifacts +dist diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..c843044 --- /dev/null +++ b/.prettierrc @@ -0,0 +1 @@ +{ "printWidth": 100 } diff --git a/jest.config.ts b/jest.config.ts index 591892d..888fc78 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -1,10 +1,8 @@ -import type { JestConfigWithTsJest } from "ts-jest" +import type { JestConfigWithTsJest } from "ts-jest"; const config: JestConfigWithTsJest = { testEnvironment: "node", - testMatch: [ - "**/test/**/*.test.ts", - ], + testMatch: ["**/test/**/*.test.ts"], // See https://kulshekhar.github.io/ts-jest/docs/guides/esm-support#esm-presets for below preset: "ts-jest/presets/default-esm", moduleNameMapper: { @@ -12,11 +10,12 @@ const config: JestConfigWithTsJest = { }, transform: { "^.+\\.tsx?$": [ - "ts-jest", { + "ts-jest", + { useESM: true, }, ], }, -} +}; -export default config +export default config; diff --git a/src/args.ts b/src/args.ts index 85eab21..aae5ae6 100644 --- a/src/args.ts +++ b/src/args.ts @@ -15,6 +15,6 @@ export const parseArgs = (args: string[]): Args => { return { watch: result.hasOwnProperty("watch"), - sri: result["sri"]?.split(",") + sri: result["sri"]?.split(","), }; -} +}; diff --git a/src/esbuild.ts b/src/esbuild.ts index 28fb7b1..d55a867 100644 --- a/src/esbuild.ts +++ b/src/esbuild.ts @@ -2,29 +2,29 @@ import path from "path"; import { globSync } from "glob"; import { BuildOptions, Loader } from "esbuild"; import { Args } from "./args.js"; -import hanamiEsbuild, { HanamiEsbuildPluginOptions, defaults } from './hanami-esbuild-plugin.js'; +import hanamiEsbuild, { HanamiEsbuildPluginOptions, defaults } from "./hanami-esbuild-plugin.js"; const loader: { [ext: string]: Loader } = { - '.tsx': 'tsx', - '.ts': 'ts', - '.js': 'js', - '.jsx': 'jsx', - '.json': 'json', - '.png': 'file', - '.jpg': 'file', - '.jpeg': 'file', - '.gif': 'file', - '.svg': 'file', - '.woff': 'file', - '.woff2': 'file', - '.otf': 'file', - '.eot': 'file', - '.ttf': 'file', + ".tsx": "tsx", + ".ts": "ts", + ".js": "js", + ".jsx": "jsx", + ".json": "json", + ".png": "file", + ".jpg": "file", + ".jpeg": "file", + ".gif": "file", + ".svg": "file", + ".woff": "file", + ".woff2": "file", + ".otf": "file", + ".eot": "file", + ".ttf": "file", }; const entryPointExtensions = "app.{js,ts,mjs,mts,tsx,jsx}"; // FIXME: make cross platform -const entryPointsMatcher = /(app\/assets\/js\/|slices\/(.*\/)assets\/js\/)/ +const entryPointsMatcher = /(app\/assets\/js\/|slices\/(.*\/)assets\/js\/)/; const findEntryPoints = (root: string): Record => { const result: Record = {}; @@ -36,31 +36,31 @@ const findEntryPoints = (root: string): Record => { ]); entryPoints.forEach((entryPoint) => { - let modifiedPath = entryPoint.replace(entryPointsMatcher, "$2") - const relativePath = path.relative(root, modifiedPath) + let modifiedPath = entryPoint.replace(entryPointsMatcher, "$2"); + const relativePath = path.relative(root, modifiedPath); - const { dir, name } = path.parse(relativePath) + const { dir, name } = path.parse(relativePath); if (dir) { - modifiedPath = path.join(dir, name) + modifiedPath = path.join(dir, name); } else { - modifiedPath = name + modifiedPath = name; } - result[modifiedPath] = entryPoint + result[modifiedPath] = entryPoint; }); return result; -} +}; // TODO: feels like this really should be passed a root too, to become the cwd for globSync const externalDirectories = (): string[] => { const assetDirsPattern = [ path.join("app", "assets", "*"), path.join("slices", "*", "assets", "*"), - ] + ]; - const excludeDirs = ['js', 'css']; + const excludeDirs = ["js", "css"]; try { const dirs = globSync(assetDirsPattern, { nodir: false }); @@ -71,7 +71,7 @@ const externalDirectories = (): string[] => { return filteredDirs.map((dir) => path.join(dir, "*")); } catch (err) { - console.error('Error listing external directories:', err); + console.error("Error listing external directories:", err); return []; } }; @@ -80,9 +80,9 @@ const externalDirectories = (): string[] => { export const buildOptions = (root: string, args: Args): Partial => { const pluginOptions: HanamiEsbuildPluginOptions = { ...defaults, - sriAlgorithms: args.sri || [] + sriAlgorithms: args.sri || [], }; - const plugin = hanamiEsbuild(pluginOptions) + const plugin = hanamiEsbuild(pluginOptions); const options: Partial = { bundle: true, @@ -96,7 +96,7 @@ export const buildOptions = (root: string, args: Args): Partial => entryNames: "[dir]/[name]-[hash]", entryPoints: findEntryPoints(root), plugins: [plugin], - } + }; return options; }; @@ -104,9 +104,9 @@ export const buildOptions = (root: string, args: Args): Partial => export const watchOptions = (root: string, args: Args): Partial => { const pluginOptions: HanamiEsbuildPluginOptions = { ...defaults, - hash: false + hash: false, }; - const plugin = hanamiEsbuild(pluginOptions) + const plugin = hanamiEsbuild(pluginOptions); const options: Partial = { bundle: true, @@ -120,8 +120,7 @@ export const watchOptions = (root: string, args: Args): Partial => entryNames: "[dir]/[name]", entryPoints: findEntryPoints(root), plugins: [plugin], - } + }; return options; -} - +}; diff --git a/src/hanami-assets.ts b/src/hanami-assets.ts index 1c48206..1971210 100644 --- a/src/hanami-assets.ts +++ b/src/hanami-assets.ts @@ -1,34 +1,39 @@ #!/usr/bin/env node -import fs from 'fs-extra'; -import path from 'path'; +import fs from "fs-extra"; +import path from "path"; import esbuild, { BuildOptions, BuildContext } from "esbuild"; import { Args, parseArgs } from "./args.js"; -import { buildOptions, watchOptions } from './esbuild.js'; +import { buildOptions, watchOptions } from "./esbuild.js"; type RunOptionsFunction = (args: Args, options: Partial) => Partial; -export const run = async function(root: string, argv: string[], optionsFunction?: RunOptionsFunction): Promise { +export const run = async function ( + root: string, + argv: string[], + optionsFunction?: RunOptionsFunction, +): Promise { const args = parseArgs(argv); // TODO: make nicer let esbuildOptions = args.watch ? watchOptions(root, args) : buildOptions(root, args); - if (optionsFunction) { esbuildOptions = optionsFunction(args, esbuildOptions); } + if (optionsFunction) { + esbuildOptions = optionsFunction(args, esbuildOptions); + } const errorHandler = (err: any): void => { console.log(err); process.exit(1); - } + }; if (args.watch) { touchManifest(root); - const ctx = await esbuild.context(esbuildOptions) - await ctx.watch().catch(errorHandler) + const ctx = await esbuild.context(esbuildOptions); + await ctx.watch().catch(errorHandler); return ctx; - } - else { + } else { await esbuild.build(esbuildOptions).catch(errorHandler); } }; @@ -40,4 +45,4 @@ const touchManifest = (root: string): void => { fs.ensureDirSync(manifestDir); fs.writeFileSync(manifestPath, JSON.stringify({}, null, 2)); -} +}; diff --git a/src/hanami-esbuild-plugin.ts b/src/hanami-esbuild-plugin.ts index b59a9d8..716d622 100644 --- a/src/hanami-esbuild-plugin.ts +++ b/src/hanami-esbuild-plugin.ts @@ -1,13 +1,9 @@ -import { - BuildResult, - Plugin, - PluginBuild -} from 'esbuild'; -import fs from 'fs-extra'; -import path from 'path'; -import crypto from 'node:crypto'; +import { BuildResult, Plugin, PluginBuild } from "esbuild"; +import fs from "fs-extra"; +import path from "path"; +import crypto from "node:crypto"; -const URL_SEPARATOR = '/'; +const URL_SEPARATOR = "/"; export interface HanamiEsbuildPluginOptions { root: string; @@ -18,11 +14,14 @@ export interface HanamiEsbuildPluginOptions { hash: boolean; } -export const defaults: Pick = { - root: '', - publicDir: 'public', - destDir: path.join('public', 'assets'), - manifestPath: path.join('public', 'assets.json'), +export const defaults: Pick< + HanamiEsbuildPluginOptions, + "root" | "publicDir" | "destDir" | "manifestPath" | "sriAlgorithms" | "hash" +> = { + root: "", + publicDir: "public", + destDir: path.join("public", "assets"), + manifestPath: path.join("public", "assets.json"), sriAlgorithms: [], hash: true, }; @@ -34,7 +33,7 @@ interface Asset { const hanamiEsbuild = (options: HanamiEsbuildPluginOptions = { ...defaults }): Plugin => { return { - name: 'hanami-esbuild', + name: "hanami-esbuild", setup(build: PluginBuild) { build.initialOptions.metafile = true; @@ -48,23 +47,25 @@ const hanamiEsbuild = (options: HanamiEsbuildPluginOptions = { ...defaults }): P const assetsManifest: Record = {}; const calulateSourceUrl = (str: string): string => { - return normalizeUrl(str).replace(/\/assets\//, '').replace(/-[A-Z0-9]{8}/, ''); - } + return normalizeUrl(str) + .replace(/\/assets\//, "") + .replace(/-[A-Z0-9]{8}/, ""); + }; const calulateDestinationUrl = (str: string): string => { - return normalizeUrl(str).replace(/public/, ''); - } + return normalizeUrl(str).replace(/public/, ""); + }; const normalizeUrl = (str: string): string => { return str.replace(/[\\]+/, URL_SEPARATOR); - } + }; const calculateSubresourceIntegrity = (algorithm: string, path: string): string => { - const content = fs.readFileSync(path, 'utf8'); - const hash = crypto.createHash(algorithm).update(content).digest('base64'); + const content = fs.readFileSync(path, "utf8"); + const hash = crypto.createHash(algorithm).update(content).digest("base64"); return `${algorithm}-${hash}`; - } + }; // Inspired by https://github.com/evanw/esbuild/blob/2f2b90a99d626921d25fe6d7d0ca50bd48caa427/internal/bundler/bundler.go#L1057 const calculateHash = (hashBytes: Uint8Array, hash: boolean): string | null => { @@ -72,10 +73,10 @@ const hanamiEsbuild = (options: HanamiEsbuildPluginOptions = { ...defaults }): P return null; } - const result = crypto.createHash('sha256').update(hashBytes).digest('hex'); + const result = crypto.createHash("sha256").update(hashBytes).digest("hex"); return result.slice(0, 8).toUpperCase(); - } + }; function extractEsbuildInputs(inputData: Record): Record { const inputs: Record = {}; @@ -114,7 +115,11 @@ const hanamiEsbuild = (options: HanamiEsbuildPluginOptions = { ...defaults }): P return; }; - const processAssetDirectory = (pattern: string, inputs: Record, options: HanamiEsbuildPluginOptions): string[] => { + const processAssetDirectory = ( + pattern: string, + inputs: Record, + options: HanamiEsbuildPluginOptions, + ): string[] => { const dirPath = path.dirname(pattern); const files = fs.readdirSync(dirPath); const assets: string[] = []; @@ -130,8 +135,12 @@ const hanamiEsbuild = (options: HanamiEsbuildPluginOptions = { ...defaults }): P const fileHash = calculateHash(fs.readFileSync(srcPath), options.hash); const fileExtension = path.extname(srcPath); const baseName = path.basename(srcPath, fileExtension); - const destFileName = [baseName, fileHash].filter(item => item !== null).join("-") + fileExtension; - const destPath = path.join(options.destDir, path.relative(dirPath, srcPath).replace(file, destFileName)); + const destFileName = + [baseName, fileHash].filter((item) => item !== null).join("-") + fileExtension; + const destPath = path.join( + options.destDir, + path.relative(dirPath, srcPath).replace(file, destFileName), + ); if (fs.lstatSync(srcPath).isDirectory()) { assets.push(...processAssetDirectory(destPath, inputs, options)); @@ -144,7 +153,7 @@ const hanamiEsbuild = (options: HanamiEsbuildPluginOptions = { ...defaults }): P return assets; }; - if (typeof outputs === 'undefined') { + if (typeof outputs === "undefined") { return; } @@ -157,7 +166,7 @@ const hanamiEsbuild = (options: HanamiEsbuildPluginOptions = { ...defaults }): P const assetsToProcess = Object.keys(outputs).concat(copiedAssets); for (const assetToProcess of assetsToProcess) { - if (assetToProcess.endsWith('.map')) { + if (assetToProcess.endsWith(".map")) { continue; } diff --git a/test/fixtures/todo/app/assets/css/app.css b/test/fixtures/todo/app/assets/css/app.css index d309294..0826167 100644 --- a/test/fixtures/todo/app/assets/css/app.css +++ b/test/fixtures/todo/app/assets/css/app.css @@ -1,8 +1,8 @@ @import "reset.css"; body { - background-image: url('../images/background.jpg'); - font-family: 'Custom Font', sans-serif; + background-image: url("../images/background.jpg"); + font-family: "Custom Font", sans-serif; color: white; margin: 0; padding: 0; @@ -20,7 +20,7 @@ form { margin-bottom: 20px; } -input[type='text'] { +input[type="text"] { padding: 10px; font-size: 16px; border: none; @@ -29,7 +29,7 @@ input[type='text'] { flex-grow: 1; } -button[type='submit'] { +button[type="submit"] { padding: 10px; background-color: #48bb78; color: white; diff --git a/test/fixtures/todo/app/assets/css/reset.css b/test/fixtures/todo/app/assets/css/reset.css index 49de4b2..101e4d1 100644 --- a/test/fixtures/todo/app/assets/css/reset.css +++ b/test/fixtures/todo/app/assets/css/reset.css @@ -125,7 +125,7 @@ blockquote:before, blockquote:after, q:before, q:after { - content: ''; + content: ""; content: none; } diff --git a/test/fixtures/todo/app/assets/js/TodoItem.tsx b/test/fixtures/todo/app/assets/js/TodoItem.tsx index 818d289..e991586 100644 --- a/test/fixtures/todo/app/assets/js/TodoItem.tsx +++ b/test/fixtures/todo/app/assets/js/TodoItem.tsx @@ -1,5 +1,5 @@ -import React from 'react'; -import { Todo } from './TodoList'; +import React from "react"; +import { Todo } from "./TodoList"; interface Props { todo: Todo; @@ -10,11 +10,9 @@ interface Props { const TodoItem = ({ todo, onComplete, onDelete }: Props) => { return (
  • - {todo.text} + {todo.text}
    - +
  • diff --git a/test/fixtures/todo/app/assets/js/TodoList.tsx b/test/fixtures/todo/app/assets/js/TodoList.tsx index 3d8e3ae..ede12f1 100644 --- a/test/fixtures/todo/app/assets/js/TodoList.tsx +++ b/test/fixtures/todo/app/assets/js/TodoList.tsx @@ -1,5 +1,5 @@ -import React, { useState } from 'react'; -import TodoItem from './TodoItem'; +import React, { useState } from "react"; +import TodoItem from "./TodoItem"; interface Todo { id: number; @@ -9,21 +9,19 @@ interface Todo { const TodoList = () => { const [todos, setTodos] = useState([]); - const [text, setText] = useState(''); + const [text, setText] = useState(""); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (!text.trim()) return; const newTodo: Todo = { id: Date.now(), text, completed: false }; setTodos([...todos, newTodo]); - setText(''); + setText(""); }; const handleComplete = (id: number) => { setTodos((prevTodos) => - prevTodos.map((todo) => - todo.id === id ? { ...todo, completed: !todo.completed } : todo - ) + prevTodos.map((todo) => (todo.id === id ? { ...todo, completed: !todo.completed } : todo)), ); }; diff --git a/test/fixtures/todo/app/assets/js/app.tsx b/test/fixtures/todo/app/assets/js/app.tsx index ec28b78..3476657 100644 --- a/test/fixtures/todo/app/assets/js/app.tsx +++ b/test/fixtures/todo/app/assets/js/app.tsx @@ -1,7 +1,7 @@ -import React from 'react'; -import ReactDOM from 'react-dom'; -import '../css/app.css'; // Import the main stylesheet -import TodoList from './TodoList'; +import React from "react"; +import ReactDOM from "react-dom"; +import "../css/app.css"; // Import the main stylesheet +import TodoList from "./TodoList"; const App = () => { return ( @@ -12,4 +12,4 @@ const App = () => { ); }; -ReactDOM.render(, document.getElementById('root')); +ReactDOM.render(, document.getElementById("root")); diff --git a/test/fixtures/todo/app/assets/js/login/app.tsx b/test/fixtures/todo/app/assets/js/login/app.tsx index c4da5a5..1d5a5ac 100644 --- a/test/fixtures/todo/app/assets/js/login/app.tsx +++ b/test/fixtures/todo/app/assets/js/login/app.tsx @@ -1 +1 @@ -console.log('login'); +console.log("login"); diff --git a/test/fixtures/todo/slices/admin/assets/js/app.js b/test/fixtures/todo/slices/admin/assets/js/app.js index 97544ce..358a8b5 100644 --- a/test/fixtures/todo/slices/admin/assets/js/app.js +++ b/test/fixtures/todo/slices/admin/assets/js/app.js @@ -1 +1 @@ -console.log('admin'); +console.log("admin"); diff --git a/test/hanami-assets.test.ts b/test/hanami-assets.test.ts index fb0424c..d85270b 100644 --- a/test/hanami-assets.test.ts +++ b/test/hanami-assets.test.ts @@ -1,21 +1,21 @@ -import fs from 'fs-extra'; -import path from 'path'; -import { globSync } from 'glob' -import crypto from 'node:crypto'; +import fs from "fs-extra"; +import path from "path"; +import { globSync } from "glob"; +import crypto from "node:crypto"; import * as assets from "../src/hanami-assets"; const originalWorkingDir = process.cwd(); -const dest = path.resolve(__dirname, '..', 'tmp', crypto.randomUUID()); +const dest = path.resolve(__dirname, "..", "tmp", crypto.randomUUID()); const watchTimeout = 60000; // ms (60 seconds) // Helper function to create a test environment async function createTestEnvironment() { // Create temporary directories - await fs.ensureDir(path.join(dest, 'app/assets/js')); - await fs.ensureDir(path.join(dest, 'app/assets/images')); - await fs.ensureDir(path.join(dest, 'slices/admin/assets/js')); - await fs.ensureDir(path.join(dest, 'slices/metrics/assets/js')); - await fs.ensureDir(path.join(dest, 'public')); + await fs.ensureDir(path.join(dest, "app/assets/js")); + await fs.ensureDir(path.join(dest, "app/assets/images")); + await fs.ensureDir(path.join(dest, "slices/admin/assets/js")); + await fs.ensureDir(path.join(dest, "slices/metrics/assets/js")); + await fs.ensureDir(path.join(dest, "public")); process.chdir(dest); } @@ -26,7 +26,7 @@ async function cleanTestEnvironment() { await fs.remove(dest); // Comment this line to manually inspect precompile results } -describe('hanami-assets', () => { +describe("hanami-assets", () => { beforeEach(async () => { await createTestEnvironment(); }); @@ -35,191 +35,185 @@ describe('hanami-assets', () => { await cleanTestEnvironment(); }); - test('copies assets from app/assets to public/assets and generates a manifest file', async () => { - const entryPoint1 = path.join(dest, 'app/assets/js/app.js'); - const entryPoint2 = path.join(dest, 'slices/admin/assets/js/app.js'); - const entryPoint3 = path.join(dest, 'slices/metrics/assets/js/app.ts'); + test("copies assets from app/assets to public/assets and generates a manifest file", async () => { + const entryPoint1 = path.join(dest, "app/assets/js/app.js"); + const entryPoint2 = path.join(dest, "slices/admin/assets/js/app.js"); + const entryPoint3 = path.join(dest, "slices/metrics/assets/js/app.ts"); await fs.writeFile(entryPoint1, "console.log('Hello, World!');"); await fs.writeFile(entryPoint2, "console.log('Hello, Admin!');"); await fs.writeFile(entryPoint3, "console.log('Hello, Metrics!');"); // Compile assets - await assets.run(dest, []) + await assets.run(dest, []); // FIXME: this path should take into account the file hashing in the file name - const appAsset = globSync(path.join('public/assets/app-*.js'))[0] + const appAsset = globSync(path.join("public/assets/app-*.js"))[0]; const appAssetExists = await fs.pathExists(appAsset); expect(appAssetExists).toBe(true); // FIXME: this path should take into account the file hashing in the file name - const sliceAsset1 = globSync(path.join('public/assets/admin/app-*.js'))[0]; + const sliceAsset1 = globSync(path.join("public/assets/admin/app-*.js"))[0]; const sliceAssetExists1 = await fs.pathExists(sliceAsset1); expect(sliceAssetExists1).toBe(true); // FIXME: this path should take into account the file hashing in the file name - const sliceAsset2 = globSync(path.join('public/assets/metrics/app-*.js'))[0]; + const sliceAsset2 = globSync(path.join("public/assets/metrics/app-*.js"))[0]; const sliceAssetExists2 = await fs.pathExists(sliceAsset2); expect(sliceAssetExists2).toBe(true); - const manifestExists = await fs.pathExists(path.join(dest, 'public/assets.json')); + const manifestExists = await fs.pathExists(path.join(dest, "public/assets.json")); expect(manifestExists).toBe(true); // Read and parse the manifest file - const manifestContent = await fs.readFile(path.join(dest, 'public/assets.json'), 'utf-8'); + const manifestContent = await fs.readFile(path.join(dest, "public/assets.json"), "utf-8"); const manifest = JSON.parse(manifestContent); // Check if the manifest contains the correct file paths expect(manifest).toEqual({ "admin/app.js": { - "url": "/assets/admin/app-NLRESL5A.js", + url: "/assets/admin/app-NLRESL5A.js", }, "app.js": { - "url": "/assets/app-JLSTK5SN.js", + url: "/assets/app-JLSTK5SN.js", }, "metrics/app.js": { - "url": "/assets/metrics/app-27Z7ZALS.js", - } + url: "/assets/metrics/app-27Z7ZALS.js", + }, }); }); - test('generates SRI', async () => { - const entryPoint1 = path.join(dest, 'app/assets/js/app.js'); + test("generates SRI", async () => { + const entryPoint1 = path.join(dest, "app/assets/js/app.js"); await fs.writeFile(entryPoint1, "console.log('Hello, World!');"); // Compile assets - await assets.run(dest, ["--sri=sha256,sha384,sha512"]) + await assets.run(dest, ["--sri=sha256,sha384,sha512"]); // Read and parse the manifest file - const manifestContent = await fs.readFile(path.join(dest, 'public/assets.json'), 'utf-8'); + const manifestContent = await fs.readFile(path.join(dest, "public/assets.json"), "utf-8"); const manifest = JSON.parse(manifestContent); // Check if the manifest contains the correct file paths expect(manifest).toEqual({ "app.js": { - "url": "/assets/app-JLSTK5SN.js", - "sri": [ + url: "/assets/app-JLSTK5SN.js", + sri: [ "sha256-p4j9argOiwyiBIBi7v4H0WUnv6z3kmFjqmManMEJXfo=", "sha384-gkA54jmSv7TBjiSzGrfO/uCR5CyUrQSUSUYrnM0lICIaP5ppqcN8PLVE3mNj87sN", - "sha512-ZCJAQgHAxcBP7xEuQ3W/pZqqI611aX9oEk0QvDG4Etq6bkvxQMWHrHk2npCytWchOTN1yKM7TLj9Vsp1Id0j6g==" - ] + "sha512-ZCJAQgHAxcBP7xEuQ3W/pZqqI611aX9oEk0QvDG4Etq6bkvxQMWHrHk2npCytWchOTN1yKM7TLj9Vsp1Id0j6g==", + ], }, }); }); - test('Full app', async () => { - fs.copySync(path.join(__dirname, 'fixtures', 'todo'), dest); + test("Full app", async () => { + fs.copySync(path.join(__dirname, "fixtures", "todo"), dest); // Compile assets - await assets.run(dest, ["--sri=sha384"]) + await assets.run(dest, ["--sri=sha384"]); // Read and parse the manifest file - const manifestContent = await fs.readFile(path.join(dest, 'public/assets.json'), 'utf-8'); + const manifestContent = await fs.readFile(path.join(dest, "public/assets.json"), "utf-8"); const manifest = JSON.parse(manifestContent); // Check if the manifest contains the correct file paths expect(manifest).toEqual({ "app.js": { - "url": "/assets/app-QFCHLRMD.js", - "sri": [ - "sha384-tn8zwUHIJjEPGmD6vZ2DKmY6LlbNkI86ltpSiBxcCICkETm5g4sMDV0oF2I+8xDz" - ] + url: "/assets/app-QFCHLRMD.js", + sri: ["sha384-tn8zwUHIJjEPGmD6vZ2DKmY6LlbNkI86ltpSiBxcCICkETm5g4sMDV0oF2I+8xDz"], }, "background.jpg": { - "url": "/assets/background-UU2XY655.jpg", - "sri": [ - "sha384-M7QyKTUfzyVWNC4FoMYq0ypu7LDifAYWEtXRT5d6M3Prpau9t5wavW1216HhvCJc" - ] + url: "/assets/background-UU2XY655.jpg", + sri: ["sha384-M7QyKTUfzyVWNC4FoMYq0ypu7LDifAYWEtXRT5d6M3Prpau9t5wavW1216HhvCJc"], }, "app.css": { - "url": "/assets/app-REBB4IZN.css", - "sri": [ - "sha384-ZqNcOOnG1W+Udcquy7qBs4b51X1cq8Bkv0CMEIt5I20+TWmW33pLUHl54ueCnyFD" - ] + url: "/assets/app-REBB4IZN.css", + sri: ["sha384-ZqNcOOnG1W+Udcquy7qBs4b51X1cq8Bkv0CMEIt5I20+TWmW33pLUHl54ueCnyFD"], }, "login/app.js": { - "url": "/assets/login/app-SV7Q442Q.js", - "sri": [ - "sha384-fkk3ZtSsrBOPtmKaOSoHC5IKdaphOeG05j0Z3iQPrJdbQAxsAmCkJMNQphDyL8E2" - ] + url: "/assets/login/app-SV7Q442Q.js", + sri: ["sha384-fkk3ZtSsrBOPtmKaOSoHC5IKdaphOeG05j0Z3iQPrJdbQAxsAmCkJMNQphDyL8E2"], }, "admin/app.js": { - "url": "/assets/admin/app-XJBJOVJT.js", - "sri": [ - "sha384-aqVgGQ+qnJfgFqVoOlHBEEQ1eUtNDN5cQu9EdUW7gIGT9VaUN8H2yw5ai+lW+jc9" - ] + url: "/assets/admin/app-XJBJOVJT.js", + sri: ["sha384-aqVgGQ+qnJfgFqVoOlHBEEQ1eUtNDN5cQu9EdUW7gIGT9VaUN8H2yw5ai+lW+jc9"], }, "font.otf": { - "url": "/assets/font-E1A70B27.otf", - "sri": [ - "sha384-Lpm/oUsCQkOg41WyENyyB1zjaX/FB522VWlU44JKakwzwBxvu11le0ILkiPsR73K" - ] + url: "/assets/font-E1A70B27.otf", + sri: ["sha384-Lpm/oUsCQkOg41WyENyyB1zjaX/FB522VWlU44JKakwzwBxvu11le0ILkiPsR73K"], }, "logo.png": { - "url": "/assets/logo-C1EF77E4.png", - "sri": [ - "sha384-7q5x+ZjZrCoWwyV0BTyc8HUPf1xr+n9l77gwxmwywPWSe0PtopZj1T8NTUPFo0FI" - ] - } + url: "/assets/logo-C1EF77E4.png", + sri: ["sha384-7q5x+ZjZrCoWwyV0BTyc8HUPf1xr+n9l77gwxmwywPWSe0PtopZj1T8NTUPFo0FI"], + }, }); }); - test("watch", async () => { - const images = path.join(dest, "app", "assets", "images"); - await fs.ensureDir(images); - fs.copySync(path.join(__dirname, "fixtures", "todo", "app", "assets", "images", "background.jpg"), path.join(images, "background.jpg")); + test( + "watch", + async () => { + const images = path.join(dest, "app", "assets", "images"); + await fs.ensureDir(images); + fs.copySync( + path.join(__dirname, "fixtures", "todo", "app", "assets", "images", "background.jpg"), + path.join(images, "background.jpg"), + ); - const entryPoint = path.join(dest, "app", "assets", "js", "app.js"); - await fs.writeFile(entryPoint, "console.log('Hello, World!');"); + const entryPoint = path.join(dest, "app", "assets", "js", "app.js"); + await fs.writeFile(entryPoint, "console.log('Hello, World!');"); - const appAsset = path.join(dest, "public", "assets", "app.js"); - const imageAsset = path.join(dest, "public", "assets", "background.jpg"); + const appAsset = path.join(dest, "public", "assets", "app.js"); + const imageAsset = path.join(dest, "public", "assets", "background.jpg"); - // Watch for asset changes - let ctx = await assets.run(dest, ["--watch"]) + // Watch for asset changes + let ctx = await assets.run(dest, ["--watch"]); - await fs.writeFile(entryPoint, "console.log('Hello, Watch!');"); + await fs.writeFile(entryPoint, "console.log('Hello, Watch!');"); - const appAssetExists = (timeout = watchTimeout): Promise => { - return new Promise((resolve, reject) => { - let elapsedTime = 0; - const intervalTime = 100; + const appAssetExists = (timeout = watchTimeout): Promise => { + return new Promise((resolve, reject) => { + let elapsedTime = 0; + const intervalTime = 100; - const interval = setInterval(() => { - if (fs.existsSync(appAsset)) { - clearInterval(interval); - resolve(true); - } - - elapsedTime += intervalTime; - if (elapsedTime >= timeout) { - clearInterval(interval); - reject(false); - } - }, intervalTime); - }); - } + const interval = setInterval(() => { + if (fs.existsSync(appAsset)) { + clearInterval(interval); + resolve(true); + } - const found = await appAssetExists(); - expect(found).toBe(true); + elapsedTime += intervalTime; + if (elapsedTime >= timeout) { + clearInterval(interval); + reject(false); + } + }, intervalTime); + }); + }; - expect(fs.existsSync(imageAsset)).toBe(true); + const found = await appAssetExists(); + expect(found).toBe(true); - // Read the asset file - const assetContent = await fs.readFile(appAsset, "utf-8"); + expect(fs.existsSync(imageAsset)).toBe(true); - // Check if the asset has the expected contents - expect(assetContent).toMatch("console.log(\"Hello, Watch!\");"); + // Read the asset file + const assetContent = await fs.readFile(appAsset, "utf-8"); - const manifestExists = await fs.pathExists(path.join(dest, 'public/assets.json')); - expect(manifestExists).toBe(true); + // Check if the asset has the expected contents + expect(assetContent).toMatch('console.log("Hello, Watch!");'); - // Read and parse the manifest file - const manifestContent = await fs.readFile(path.join(dest, 'public/assets.json'), 'utf-8'); - const manifest = JSON.parse(manifestContent); + const manifestExists = await fs.pathExists(path.join(dest, "public/assets.json")); + expect(manifestExists).toBe(true); - expect(manifest["background.jpg"]).toEqual({ "url": "/assets/background.jpg" }) + // Read and parse the manifest file + const manifestContent = await fs.readFile(path.join(dest, "public/assets.json"), "utf-8"); + const manifest = JSON.parse(manifestContent); - await ctx!.dispose() + expect(manifest["background.jpg"]).toEqual({ + url: "/assets/background.jpg", + }); - }, watchTimeout + 1000); + await ctx!.dispose(); + }, + watchTimeout + 1000, + ); });