diff --git a/jest.config.js b/jest.config.js deleted file mode 100644 index d66adb7..0000000 --- a/jest.config.js +++ /dev/null @@ -1,11 +0,0 @@ -/** @type {import('@jest/types').Config.InitialOptions} */ -const config = { - testEnvironment: 'node', - verbose: true, - testMatch: [ - '**/__tests__/**/*.js', - '**/?(*.)+(spec|test).js', - '**/test/*.js', - ], -}; -module.exports = config; diff --git a/readme.md b/readme.md index d6236b6..c938762 100644 --- a/readme.md +++ b/readme.md @@ -202,6 +202,19 @@ Default: `'module-to-cdn'` Allow you to define a custom module resolver, it can either be a `function` or an npm module. The resolver should return (or resolve as a Promise) either `null` or an `object` with the keys: `name`, `var`, `url`, `version`. +#### options.html +Type: `boolean`
+Default: `!loadScripts` (`true`, unless `options.loadScripts` is true) + +Inject the CDN script tags into the `HtmlWebpackPlugin`, if available + +#### options.loadScripts +Type: `boolean`
+Default: `false` + +Instead of expecting the scripts to already be loaded via a `` in the html, load them dynamically. +Uses [webpack externalsType.script](https://webpack.js.org/configuration/externals/#externalstypescript) + ## Related diff --git a/src/declarations.d.ts b/src/declarations.d.ts deleted file mode 100644 index 52504d8..0000000 --- a/src/declarations.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -declare module 'webpack/lib/ExternalModule' { - import { ExternalModule as ExternalModuleType } from 'webpack'; - declare class ExternalModule extends ExternalModuleType {} - export default ExternalModule; -} diff --git a/src/index.ts b/src/index.ts index 7bbec9d..d194ed7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,8 +1,8 @@ import type HtmlWebpackPlugin from 'html-webpack-plugin'; import readPkgUp from 'read-pkg-up'; import resolvePkg from 'resolve-pkg'; +import { ExternalModule } from 'webpack'; import type { Compiler } from 'webpack'; -import ExternalModule from 'webpack/lib/ExternalModule'; import getResolver from './get-resolver'; import { CdnModuleInfo, CdnModuleResolver, PluginOptions } from './types'; @@ -28,12 +28,23 @@ export default class DynamicCdnWebpackPlugin { verbose: boolean; resolver: CdnModuleResolver; modulesFromCdn: { [modulePath: string]: CdnModuleInfo }; + loadScripts: boolean; htmlWebpackPlugin?: typeof HtmlWebpackPlugin; + constructor( options: PluginOptions = {}, htmlWebpackPlugin?: typeof HtmlWebpackPlugin | false ) { - const { disable = false, env, exclude, only, verbose, resolver } = options; + const { + disable = false, + env, + exclude, + only, + verbose, + resolver, + loadScripts = false, + html = !loadScripts, + } = options; if (exclude && only) { throw new Error("You can't use 'exclude' and 'only' at the same time"); } @@ -44,9 +55,9 @@ export default class DynamicCdnWebpackPlugin { this.only = only; this.verbose = verbose === true; this.resolver = getResolver(resolver); + this.loadScripts = loadScripts; this.modulesFromCdn = {}; - // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing - this.htmlWebpackPlugin = htmlWebpackPlugin || undefined; + this.htmlWebpackPlugin = (html && htmlWebpackPlugin) || undefined; // Support for old way without passing htmlWebpackPlugin as an argument if (htmlWebpackPlugin === undefined) { @@ -71,10 +82,14 @@ export default class DynamicCdnWebpackPlugin { if (!moduleRegex.test(modulePath)) return; // Use recognized CDN module if found - const varName = await this.addModule(contextPath, modulePath, { env }); - return typeof varName === 'string' - ? new ExternalModule(varName, 'var', modulePath) - : undefined; + const info = await this.addModule(contextPath, modulePath, { env }); + if (!info) return; + + const varName = info.var; + + return this.loadScripts + ? new ExternalModule([info.url, varName], 'script', modulePath) + : new ExternalModule(varName, 'var', modulePath); }); }); @@ -116,7 +131,7 @@ export default class DynamicCdnWebpackPlugin { contextPath: string, modulePath: string, { env }: { env: string } - ) { + ): Promise { const isModuleExcluded = this.exclude.includes(modulePath) || (this.only && !this.only.includes(modulePath)); @@ -146,7 +161,7 @@ export default class DynamicCdnWebpackPlugin { const isModuleAlreadyLoaded = Boolean(this.modulesFromCdn[modulePath]); if (isModuleAlreadyLoaded) { const isSameVersion = this.modulesFromCdn[modulePath].version === version; - return isSameVersion ? this.modulesFromCdn[modulePath].var : false; + return isSameVersion ? this.modulesFromCdn[modulePath] : false; } const cdnConfig = await this.resolver(modulePath, version, { env }); @@ -180,6 +195,6 @@ export default class DynamicCdnWebpackPlugin { } this.modulesFromCdn[modulePath] = cdnConfig; - return cdnConfig.var; + return cdnConfig; } } diff --git a/src/types.ts b/src/types.ts index 325ae7a..2590ec6 100644 --- a/src/types.ts +++ b/src/types.ts @@ -22,12 +22,35 @@ export interface CdnModuleInfo { } export interface PluginOptions { + /** Useful when working offline, will fallback to webpack normal behaviour */ disable?: boolean; + /** + * Determine if it should load the development or the production version of modules + * @default `mode` + */ env?: string; + /** List the only modules that should be served by the cdn */ only?: string[]; + /** List the modules that will always be bundled (not be served by the cdn) */ exclude?: string[]; + /** Log whether the library is being served by the cdn or is bundled */ verbose?: boolean; + /** + * Allow you to define a custom module resolver, it can either be a `function` or an npm module. + * The resolver should return (or resolve as a Promise) either `null` or an `object` with the keys: `name`, `var`, `url`, `version`. + * @default require('module-to-cdn') + */ resolver?: string | CdnModuleResolver; + /** + * Inject the CDN script tags into the `HtmlWebpackPlugin`, if available + * @default !loadScripts // (`true`, unless `options.loadScripts` is true) + */ + html?: boolean; + /** + * Instead of expecting the scripts to already be loaded via a `` in the html, load them dynamically. + * @see https://webpack.js.org/configuration/externals/#externalstypescript Uses webpack externalsType.script + */ + loadScripts?: boolean; } declare module 'webpack' {