Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add assets versioning #3

Merged
merged 1 commit into from
Apr 3, 2022
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 80 additions & 19 deletions src/bundle.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { promises as fs } from 'fs';
import { join } from 'path';
import { join , parse} from 'path';
import AdminJS, { AdminJSOptions } from 'adminjs';

process.env.ADMIN_JS_SKIP_BUNDLE = 'false';
Expand All @@ -17,43 +17,43 @@ const DESIGN_SYSTEM_DIR_PATH = 'node_modules/@adminjs/design-system';
*/
export type BundleConfig = {
/**
* File path where the bundled files should be moved into.
*
* File path where the bundled files should be moved into.
*
* The path is relative to where you run the script.
*/
destinationDir: string;
/**
* File path where custom components are bundled. If you have
* custom components in your project, they must be bundled in one single file.
* Please look at the example in the repository.
*
*
* The path is relative to where you run the script.
*/
customComponentsInitializationFilePath: string;
/**
* File path where AdminJS entry files are generated.
*
*
* This defaults to '.adminjs'.
* Set this only if you know what you're doing.
*
*
* The path is relative to where you run the script.
*/
adminJsLocalDir?: string;
/**
* File path where AdminJS standard bundle files are located.
*
*
* This defaults to 'node_modules/adminjs/lib/frontend/assets/scripts'.
* Set this only if you know what you're doing.
*
*
* The path is relative to where you run the script.
*/
adminJsAssetsDir?: string;
/**
* File path where AdminJS design system bundle files are located.
*
*
* This defaults to 'node_modules/@adminjs/design-system'.
* Set this only if you know what you're doing.
*
*
* The path is relative to where you run the script.
*/
designSystemDir?: string;
Expand All @@ -62,8 +62,31 @@ export type BundleConfig = {
* packages with custom components. It's enough to include only `resources` section.
*/
adminJsOptions?: AdminJSOptions;
/**
* You can define "versioning" if you want your assets to be versioned, e. g.
* 'app.bundle.123456.js'. Please note that this requires AdminJS version >= 5.8.0
*
* This will generate a JSON manifest file under specified path (relative to where you run the command).
*
* The generated file should be linked to `assets.coreScripts` in your
* AdminJS options object.
*/
versioning?: AssetsVersioning;
};

/**
* Versioning configuration
*
* @memberof module:@adminjs/bundler
* @alias AssetsVersioning
*/
export type AssetsVersioning = {
/**
* Path where you would like your AdminJS assets-manifest file to be saved.
*/
manifestPath: string;
}

/**
* AdminJS file config
*
Expand All @@ -85,6 +108,27 @@ export type BundleFile = {
destinationPath: string;
};

const getDestinationPath = (
asset: string,
timestamp?: number | null,
): string => {
if (!timestamp) return asset;

const { ext, name } = parse(asset);

return `${name}.${timestamp}${ext}`;
};

const createAssetsManifest = (files: BundleFile[]): string => {
const coreScripts = files.reduce((memo, { destinationPath, name }) => {
memo[name] = parse(destinationPath).base;

return memo;
}, {});

return JSON.stringify(coreScripts);
};

/**
* Bundles AdminJS javascript browser files. This is an alternative to bundling those files on server startup.
* The bundled files are stored in "destinationDir". Afterwards, you can for example:
Expand All @@ -98,11 +142,11 @@ export type BundleFile = {
* ...
* const adminJs = new AdminJS({ assetsCDN: <your server's url> })
* ```
*
*
* IMPORTANT: To prevent AdminJS from attempting to generate a new bundle on server startup,
* you must set `ADMIN_JS_SKIP_BUNDLE="true"` environment variable!
*
*
*
*
* @param {BundleConfig} options
* @memberof module:@adminjs/bundler
* @method
Expand All @@ -119,39 +163,41 @@ export type BundleFile = {
* console.log(files);
* // do something with built files here
* })();
*
*
*/
const bundle = async ({
destinationDir,
customComponentsInitializationFilePath,
adminJsLocalDir = ADMINJS_LOCAL_DIR_PATH,
adminJsAssetsDir = ADMINJS_ASSETS_DIR_PATH,
designSystemDir = DESIGN_SYSTEM_DIR_PATH,
adminJsOptions = {}
adminJsOptions = {},
versioning,
}: BundleConfig): Promise<BundleFile[]> => {
await import(join(process.cwd(), customComponentsInitializationFilePath));

const timestamp = versioning?.manifestPath ? Date.now() : null;
await fs.mkdir(join(process.cwd(), destinationDir), { recursive: true });
const files = [
{
name: 'components.bundle.js',
sourcePath: join(process.cwd(), `${adminJsLocalDir}/bundle.js`),
destinationPath: join(process.cwd(), destinationDir, 'components.bundle.js')
destinationPath: join(process.cwd(), destinationDir, getDestinationPath('components.bundle.js', timestamp))
},
{
name: 'app.bundle.js',
sourcePath: join(process.cwd(), `${adminJsAssetsDir}/app-bundle.production.js`),
destinationPath: join(process.cwd(), destinationDir, 'app.bundle.js'),
destinationPath: join(process.cwd(), destinationDir, getDestinationPath('app.bundle.js', timestamp)),
},
{
name: 'global.bundle.js',
sourcePath: join(process.cwd(), `${adminJsAssetsDir}/global-bundle.production.js`),
destinationPath: join(process.cwd(), destinationDir, 'global.bundle.js')
destinationPath: join(process.cwd(), destinationDir, getDestinationPath('global.bundle.js', timestamp))
},
{
name: 'design-system.bundle.js',
sourcePath: join(process.cwd(), `${designSystemDir}/bundle.production.js`),
destinationPath: join(process.cwd(), destinationDir, 'design-system.bundle.js')
destinationPath: join(process.cwd(), destinationDir, getDestinationPath('design-system.bundle.js', timestamp))
},
];

Expand All @@ -168,6 +214,21 @@ const bundle = async ({
customComponentsBundle.destinationPath,
);

if (versioning?.manifestPath) {
const manifestContents = createAssetsManifest(files);
const { ext } = parse(versioning?.manifestPath);

if (ext !== '.json') {
await Promise.all(files.map(({ destinationPath }) => fs.unlink(destinationPath)));
throw new Error('Invalid "versioning.manifestPath". File name must have .json extension.');
}

await fs.writeFile(
join(process.cwd(), versioning.manifestPath),
manifestContents,
);
}

console.log(`✨ Successfully built AdminJS bundle files! ✨`);

return files;
Expand Down