diff --git a/packages/webpack5/src/configuration/base.ts b/packages/webpack5/src/configuration/base.ts index 189624337..f866ac545 100644 --- a/packages/webpack5/src/configuration/base.ts +++ b/packages/webpack5/src/configuration/base.ts @@ -18,6 +18,7 @@ import { getPlatform, } from '../helpers/project'; import { hasDependency } from '../helpers/dependencies'; +import { PlatformSuffixPlugin } from '../plugins/PlatformSuffixPlugin'; export default function (config: Config, env: IWebpackEnv): Config { const entryPath = getEntryPath(); @@ -28,6 +29,10 @@ export default function (config: Config, env: IWebpackEnv): Config { // set mode config.mode(mode); + // config.stats({ + // logging: 'verbose' + // }) + // package.json is generated by the CLI with runtime options // this ensures it's not included in the bundle, but rather // resolved at runtime @@ -194,6 +199,22 @@ export default function (config: Config, env: IWebpackEnv): Config { }, ]); + // config.plugin('NormalModuleReplacementPlugin').use(NormalModuleReplacementPlugin, [ + // /.*/, + // request => { + // if (new RegExp(`\.${platform}\..+$`).test(request.request)) { + // request.rawRequest = request.rawRequest.replace(`.${platform}.`, '.') + // console.log(request) + // } + // } + // ]) + + config.plugin('PlatformSuffixPlugin').use(PlatformSuffixPlugin, [ + { + platform, + }, + ]); + // todo: refine defaults config.plugin('DefinePlugin').use(DefinePlugin, [ { diff --git a/packages/webpack5/src/configuration/javascript.ts b/packages/webpack5/src/configuration/javascript.ts index 7a87891c0..3ac0e6aef 100644 --- a/packages/webpack5/src/configuration/javascript.ts +++ b/packages/webpack5/src/configuration/javascript.ts @@ -1,4 +1,5 @@ import VirtualModulesPlugin from 'webpack-virtual-modules'; +import { ContextExclusionPlugin } from 'webpack'; import Config from 'webpack-chain'; import dedent from 'ts-dedent'; import { join } from 'path'; @@ -15,7 +16,12 @@ export default function (config: Config, env: IWebpackEnv = _env): Config { config.entry('bundle').add(virtualEntryPath); - // Add a virtual entry module + config + .plugin('ContextExclusionPluginPlugin') + .use(ContextExclusionPlugin, [/__virtual_entry__\.js$/]); + + // Add a virtual entry module that will register all modules into + // the nativescript module loader/handler config.plugin('VirtualModulesPlugin').use(VirtualModulesPlugin, [ { [virtualEntryPath]: dedent` @@ -26,6 +32,8 @@ export default function (config: Config, env: IWebpackEnv = _env): Config { }, ]); + config.resolve.extensions.add('.xml'); + // set up xml config.module .rule('xml') diff --git a/packages/webpack5/src/plugins/PlatformSuffixPlugin.ts b/packages/webpack5/src/plugins/PlatformSuffixPlugin.ts new file mode 100644 index 000000000..183d48c0f --- /dev/null +++ b/packages/webpack5/src/plugins/PlatformSuffixPlugin.ts @@ -0,0 +1,152 @@ +import { existsSync } from 'fs'; +import { extname, resolve } from 'path'; + +const id = 'PlatformSuffixPlugin'; + +interface PlatformSuffixPluginOptions { + platform: string; + // extensions: string[] | (() => string[]) +} + +export class PlatformSuffixPlugin { + private readonly platform: string; + // private readonly extensions: string[] + + constructor(options: PlatformSuffixPluginOptions) { + this.platform = options.platform; + + // if (typeof options.extensions === "function") { + // this.extensions = options.extensions() + // } else { + // this.extensions = options.extensions + // } + } + + apply(compiler: any) { + console.log( + // this.extensions, + this.platform + ); + const platformRE = new RegExp(`\.${this.platform}\.`); + + compiler.hooks.contextModuleFactory.tap(id, (cmf) => { + cmf.hooks.alternativeRequests.tap(id, (modules, options) => { + const additionalModules = []; + // we are looking for modules that are platform specific (something..ext) + // and we are duplicating them without the platform suffix + // this allows using require.context with non-existent platformless filenames + // but mapped to the platform specific variant (done in the resolver hook below) + for (const module of modules) { + if (platformRE.test(module.request)) { + additionalModules.push({ + ...module, + request: module.request.replace(platformRE, '.'), + }); + } + } + modules.push(...additionalModules); + }); + }); + + compiler.resolverFactory.hooks.resolver + .for('normal') + .tap(id, (resolver) => { + // Object.keys(resolver.hooks).forEach(hook => { + // resolver.hooks[hook].tap(id, (request, resolveContext) => { + // if( + // request?.path?.includes('foo.xml') || + // request?.request?.includes('foo.xml') + // ) { + // console.log( + // `>>> ${hook}: ${request.path}`, + // // request + // ) + // } + // // callback(); + // }); + // }) + + resolver.hooks.normalResolve.tapAsync( + id, + (request_, resolveContext, callback) => { + const { path, request } = request_; + const ext = request && extname(request); + const platformExt = ext ? `.${this.platform}${ext}` : ''; + + if (path && request && ext && !request.includes(platformExt)) { + const platformRequest = request.replace(ext, platformExt); + const extPath = resolve(path, platformRequest); + + // console.log({ + // path, + // request, + // ext, + // extPath + // }) + + // if a file with the same + a platform suffix exists + // we want to resolve that file instead + if (existsSync(extPath)) { + const message = `resolving "${request}" to "${platformRequest}"`; + const hook = resolver.ensureHook('normalResolve'); + console.log(message); + + // here we are creating a new resolve object and replacing the path + // with the .. suffix + const obj = { + ...request_, + path: resolver.join(path, platformRequest), + relativePath: + request_.relativePath && + resolver.join(request_.relativePath, platformRequest), + request: undefined, + }; + + // we call to the actual resolver to do the resolving of this new file + return resolver.doResolve( + hook, + obj, + message, + resolveContext, + callback + ); + } + } + callback(); + } + ); + // resolver.hooks.rawFile.tap(id, (request, resolveContext, callback) => { + // if(request.path && !/\.ios\..+$/.test(request.path)) { + // const { ext } = parse(request.path) + // const platformExtPath = request.path.replace(ext, `.${this.platform}${ext}`) + // // console.log({ + // // p1: request.path, + // // p2: platformExtPath + // // }) + // if(existsSync(platformExtPath)) { + // // request.path = platformExtPath + // // console.log('-'.repeat(100)) + // // console.log(request) + // const obj = { + // ...request, + // path: platformExtPath, + // fullySpecified: false + // } + // return resolver.doResolve( + // 'raw-file', + // obj, + // `resolved ${request.path} to platform specific file: ${platformExtPath}`, + // resolveContext, + // (err, result) => { + // if(err) return callback(err); + // if(result) return callback(null, result); + // return callback(); + // } + // ) + // // return request + // } + // } + // }); + }); + } +}