Skip to content

Commit

Permalink
Fix: static files were not copied after the first watch execution (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
jodosha committed Sep 8, 2023
1 parent aa64a47 commit d7ff0d4
Show file tree
Hide file tree
Showing 6 changed files with 63 additions and 39 deletions.
3 changes: 2 additions & 1 deletion dist/hanami-assets.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,9 @@ var sriAlgorithms = [];
if (args['sri']) {
sriAlgorithms = args['sri'].split(',');
}
const options = { ...hanami_esbuild_plugin_2.defaults, sriAlgorithms: sriAlgorithms };
if (watch) {
touchManifest(dest);
const options = { ...hanami_esbuild_plugin_2.defaults, hash: false };
const watchBuildOptions = {
bundle: true,
outdir: outDir,
Expand All @@ -119,6 +119,7 @@ if (watch) {
});
}
else {
const options = { ...hanami_esbuild_plugin_2.defaults, sriAlgorithms: sriAlgorithms };
const config = {
bundle: true,
outdir: outDir,
Expand Down
3 changes: 2 additions & 1 deletion dist/hanami-esbuild-plugin.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ export interface HanamiEsbuildPluginOptions {
destDir: string;
manifestPath: string;
sriAlgorithms: Array<string>;
hash: boolean;
}
export declare const defaults: Pick<HanamiEsbuildPluginOptions, 'root' | 'publicDir' | 'destDir' | 'manifestPath' | 'sriAlgorithms'>;
export declare const defaults: Pick<HanamiEsbuildPluginOptions, 'root' | 'publicDir' | 'destDir' | 'manifestPath' | 'sriAlgorithms' | 'hash'>;
declare const hanamiEsbuild: (options?: HanamiEsbuildPluginOptions) => Plugin;
export default hanamiEsbuild;
33 changes: 18 additions & 15 deletions dist/hanami-esbuild-plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ exports.defaults = {
destDir: path_1.default.join('public', 'assets'),
manifestPath: path_1.default.join('public', 'assets.json'),
sriAlgorithms: [],
hash: true,
};
const hanamiEsbuild = (options = { ...exports.defaults }) => {
return {
Expand Down Expand Up @@ -41,9 +42,12 @@ const hanamiEsbuild = (options = { ...exports.defaults }) => {
return `${algorithm}-${hash}`;
};
// Inspired by https://github.com/evanw/esbuild/blob/2f2b90a99d626921d25fe6d7d0ca50bd48caa427/internal/bundler/bundler.go#L1057
const calculateHash = (hashBytes) => {
const hash = node_crypto_1.default.createHash('sha256').update(hashBytes).digest('hex');
return hash.slice(0, 8).toUpperCase();
const calculateHash = (hashBytes, hash) => {
if (!hash) {
return null;
}
const result = node_crypto_1.default.createHash('sha256').update(hashBytes).digest('hex');
return result.slice(0, 8).toUpperCase();
};
function extractEsbuildInputs(inputData) {
const inputs = {};
Expand All @@ -57,22 +61,23 @@ const hanamiEsbuild = (options = { ...exports.defaults }) => {
}
return inputs;
}
// TODO: profile the current implementation vs blindly copying the asset
const copyAsset = (srcPath, destPath) => {
if (fs_extra_1.default.existsSync(destPath)) {
const srcStat = fs_extra_1.default.statSync(srcPath);
const destStat = fs_extra_1.default.statSync(destPath);
// File already exists and is up-to-date, skip copying
if (srcStat.mtimeMs <= destStat.mtimeMs) {
// File already exists and is up-to-date, skip copying
return false;
return;
}
}
if (!fs_extra_1.default.existsSync(path_1.default.dirname(destPath))) {
fs_extra_1.default.mkdirSync(path_1.default.dirname(destPath), { recursive: true });
}
fs_extra_1.default.copyFileSync(srcPath, destPath);
return true;
return;
};
const processAssetDirectory = (pattern, inputs) => {
const processAssetDirectory = (pattern, inputs, options) => {
const dirPath = path_1.default.dirname(pattern);
const files = fs_extra_1.default.readdirSync(dirPath);
const assets = [];
Expand All @@ -82,19 +87,17 @@ const hanamiEsbuild = (options = { ...exports.defaults }) => {
if (inputs.hasOwnProperty(srcPath)) {
return;
}
const fileHash = calculateHash(fs_extra_1.default.readFileSync(srcPath));
const fileHash = calculateHash(fs_extra_1.default.readFileSync(srcPath), options.hash);
const fileExtension = path_1.default.extname(srcPath);
const baseName = path_1.default.basename(srcPath, fileExtension);
const destFileName = `${baseName}-${fileHash}${fileExtension}`;
const destFileName = [baseName, fileHash].filter(item => item !== null).join("-") + fileExtension;
const destPath = path_1.default.join(options.destDir, path_1.default.relative(dirPath, srcPath).replace(file, destFileName));
if (fs_extra_1.default.lstatSync(srcPath).isDirectory()) {
assets.push(...processAssetDirectory(destPath, inputs));
assets.push(...processAssetDirectory(destPath, inputs, options));
}
else {
const copied = copyAsset(srcPath, destPath);
if (copied) {
assets.push(destPath);
}
copyAsset(srcPath, destPath);
assets.push(destPath);
}
});
return assets;
Expand All @@ -105,7 +108,7 @@ const hanamiEsbuild = (options = { ...exports.defaults }) => {
const inputs = extractEsbuildInputs(outputs);
const copiedAssets = [];
externalDirs.forEach((pattern) => {
copiedAssets.push(...processAssetDirectory(pattern, inputs));
copiedAssets.push(...processAssetDirectory(pattern, inputs, options));
});
const assetsToProcess = Object.keys(outputs).concat(copiedAssets);
for (const assetToProcess of assetsToProcess) {
Expand Down
6 changes: 3 additions & 3 deletions src/hanami-assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,16 +103,15 @@ const entryPoints = globSync([
const entryPointsMatcher = /(app\/assets\/javascripts\/|slices\/(.*\/)assets\/javascripts\/)/
const mappedEntryPoints = mapEntryPoints(entryPoints);
const externalDirs = externalEsbuildDirectories();
var sriAlgorithms = [] as Array<string>;
var sriAlgorithms : Array<string> = [];
if (args['sri']) {
sriAlgorithms = args['sri'].split(',');
}

const options: HanamiEsbuildPluginOptions = { ...defaults, sriAlgorithms: sriAlgorithms };

if (watch) {
touchManifest(dest);

const options: HanamiEsbuildPluginOptions = { ...defaults, hash: false };
const watchBuildOptions: Partial<BuildOptions> = {
bundle: true,
outdir: outDir,
Expand All @@ -135,6 +134,7 @@ if (watch) {
process.exit(1);
});
} else {
const options: HanamiEsbuildPluginOptions = { ...defaults, sriAlgorithms: sriAlgorithms };
const config: Partial<BuildOptions> = {
bundle: true,
outdir: outDir,
Expand Down
39 changes: 22 additions & 17 deletions src/hanami-esbuild-plugin.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,16 @@ export interface HanamiEsbuildPluginOptions {
destDir: string;
manifestPath: string;
sriAlgorithms: Array<string>;
hash: boolean;
}

export const defaults: Pick<HanamiEsbuildPluginOptions, 'root' | 'publicDir' | 'destDir' | 'manifestPath' | 'sriAlgorithms'> = {
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,
};

interface Asset {
Expand Down Expand Up @@ -65,10 +67,14 @@ const hanamiEsbuild = (options: HanamiEsbuildPluginOptions = { ...defaults }): P
}

// Inspired by https://github.com/evanw/esbuild/blob/2f2b90a99d626921d25fe6d7d0ca50bd48caa427/internal/bundler/bundler.go#L1057
const calculateHash = (hashBytes: Uint8Array): string => {
const hash = crypto.createHash('sha256').update(hashBytes).digest('hex');
const calculateHash = (hashBytes: Uint8Array, hash: boolean): string | null => {
if (!hash) {
return null;
}

const result = crypto.createHash('sha256').update(hashBytes).digest('hex');

return hash.slice(0, 8).toUpperCase();
return result.slice(0, 8).toUpperCase();
}

function extractEsbuildInputs(inputData: Record<string, any>): Record<string, boolean> {
Expand All @@ -87,14 +93,15 @@ const hanamiEsbuild = (options: HanamiEsbuildPluginOptions = { ...defaults }): P
return inputs;
}

const copyAsset = (srcPath: string, destPath: string): boolean => {
// TODO: profile the current implementation vs blindly copying the asset
const copyAsset = (srcPath: string, destPath: string): void => {
if (fs.existsSync(destPath)) {
const srcStat = fs.statSync(srcPath);
const destStat = fs.statSync(destPath);

// File already exists and is up-to-date, skip copying
if (srcStat.mtimeMs <= destStat.mtimeMs) {
// File already exists and is up-to-date, skip copying
return false;
return;
}
}

Expand All @@ -104,10 +111,10 @@ const hanamiEsbuild = (options: HanamiEsbuildPluginOptions = { ...defaults }): P

fs.copyFileSync(srcPath, destPath);

return true;
return;
};

const processAssetDirectory = (pattern: string, inputs: Record<string, boolean>): string[] => {
const processAssetDirectory = (pattern: string, inputs: Record<string, boolean>, options: HanamiEsbuildPluginOptions): string[] => {
const dirPath = path.dirname(pattern);
const files = fs.readdirSync(dirPath);
const assets: string[] = [];
Expand All @@ -120,19 +127,17 @@ const hanamiEsbuild = (options: HanamiEsbuildPluginOptions = { ...defaults }): P
return;
}

const fileHash = calculateHash(fs.readFileSync(srcPath));
const fileHash = calculateHash(fs.readFileSync(srcPath), options.hash);
const fileExtension = path.extname(srcPath);
const baseName = path.basename(srcPath, fileExtension);
const destFileName = `${baseName}-${fileHash}${fileExtension}`;
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));
assets.push(...processAssetDirectory(destPath, inputs, options));
} else {
const copied = copyAsset(srcPath, destPath);
if (copied) {
assets.push(destPath);
}
copyAsset(srcPath, destPath);
assets.push(destPath);
}
});

Expand All @@ -146,7 +151,7 @@ const hanamiEsbuild = (options: HanamiEsbuildPluginOptions = { ...defaults }): P
const inputs = extractEsbuildInputs(outputs);
const copiedAssets: string[] = [];
externalDirs.forEach((pattern) => {
copiedAssets.push(...processAssetDirectory(pattern, inputs));
copiedAssets.push(...processAssetDirectory(pattern, inputs, options));
});

const assetsToProcess = Object.keys(outputs).concat(copiedAssets);
Expand Down
18 changes: 16 additions & 2 deletions test/hanami-assets.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -168,10 +168,15 @@ describe('hanami-assets', () => {
});

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", "javascripts", "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");

watchProcess = spawn(binPath, ["--watch"], {cwd: dest});
await fs.writeFile(entryPoint, "console.log('Hello, Watch!');");
Expand All @@ -187,8 +192,6 @@ describe('hanami-assets', () => {
resolve(true);
}

// console.log("Waiting for asset to be generated...", elapsedTime, dest, appAsset);

elapsedTime += intervalTime;
if (elapsedTime >= timeout) {
clearInterval(interval);
Expand All @@ -201,12 +204,23 @@ describe('hanami-assets', () => {
const found = await appAssetExists();
expect(found).toBe(true);

expect(fs.existsSync(imageAsset)).toBe(true);

// Read the asset file
const assetContent = await fs.readFile(appAsset, "utf-8");

// Check if the asset has the expected contents
expect(assetContent).toMatch("console.log(\"Hello, Watch!\");");

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 manifest = JSON.parse(manifestContent);

expect(manifest["background.jpg"]).toEqual({"url": "/assets/background.jpg"})

// childProcess.kill("SIGHUP");
}, watchTimeout + 1000);
});

0 comments on commit d7ff0d4

Please sign in to comment.