feat(webpack): auto fallback to commonjs via runtime detection

This commit is contained in:
Nathan Walker
2025-08-28 12:15:56 -07:00
parent efbcebbbea
commit e9d3219db2
2 changed files with 85 additions and 13 deletions

View File

@@ -10,9 +10,9 @@ import TerserPlugin from 'terser-webpack-plugin';
import { getProjectFilePath, getProjectTSConfigPath } from '../helpers/project'; import { getProjectFilePath, getProjectTSConfigPath } from '../helpers/project';
import { import {
getAllDependencies,
getDependencyVersion, getDependencyVersion,
hasDependency, hasDependency,
getResolvedDependencyVersionForCheck,
} from '../helpers/dependencies'; } from '../helpers/dependencies';
import { PlatformSuffixPlugin } from '../plugins/PlatformSuffixPlugin'; import { PlatformSuffixPlugin } from '../plugins/PlatformSuffixPlugin';
import { applyFileReplacements } from '../helpers/fileReplacements'; import { applyFileReplacements } from '../helpers/fileReplacements';
@@ -40,7 +40,7 @@ export default function (config: Config, env: IWebpackEnv = _env): Config {
// set mode // set mode
config.mode(mode); config.mode(mode);
// use source map files with v9+ // use source map files by default with v9+
function useSourceMapFiles() { function useSourceMapFiles() {
if (mode === 'development') { if (mode === 'development') {
// in development we always use source-map files with v9+ runtimes // in development we always use source-map files with v9+ runtimes
@@ -48,20 +48,53 @@ export default function (config: Config, env: IWebpackEnv = _env): Config {
env.sourceMap = 'source-map'; env.sourceMap = 'source-map';
} }
} }
// determine target output by @nativescript/core version // determine target output by @nativescript/* runtime version
// v9+ supports ESM output, anything below uses CommonJS // v9+ supports ESM output, anything below uses CommonJS
if (hasDependency('@nativescript/core')) { if (
const coreVersion = getDependencyVersion('@nativescript/core'); hasDependency('@nativescript/ios') ||
// ensure alpha/beta/rc versions are considered as well hasDependency('@nativescript/visionos') ||
if (coreVersion && !coreVersion.includes('9.0.0')) { hasDependency('@nativescript/android')
if (!satisfies(coreVersion, '>=9.0.0')) { ) {
// @nativescript/core < 9 uses CommonJS output const iosVersion = getDependencyVersion('@nativescript/ios');
env.commonjs = true; const visionosVersion = getDependencyVersion('@nativescript/visionos');
} else { const androidVersion = getDependencyVersion('@nativescript/android');
if (platform === 'ios') {
const iosResolved =
getResolvedDependencyVersionForCheck('@nativescript/ios', '9.0.0') ??
iosVersion ??
undefined;
if (iosResolved && satisfies(iosResolved, '>=9.0.0')) {
useSourceMapFiles(); useSourceMapFiles();
} else {
env.commonjs = true;
}
} else if (platform === 'visionos') {
const visionosResolved =
getResolvedDependencyVersionForCheck(
'@nativescript/visionos',
'9.0.0',
) ??
visionosVersion ??
undefined;
if (visionosResolved && satisfies(visionosResolved, '>=9.0.0')) {
useSourceMapFiles();
} else {
env.commonjs = true;
}
} else if (platform === 'android') {
const androidResolved =
getResolvedDependencyVersionForCheck(
'@nativescript/android',
'9.0.0',
) ??
androidVersion ??
undefined;
if (androidResolved && satisfies(androidResolved, '>=9.0.0')) {
useSourceMapFiles();
} else {
env.commonjs = true;
} }
} else {
useSourceMapFiles();
} }
} }

View File

@@ -1,5 +1,6 @@
import path from 'path'; import path from 'path';
import { satisfies } from 'semver';
import { getPackageJson, getProjectRootPath } from './project'; import { getPackageJson, getProjectRootPath } from './project';
// todo: memoize // todo: memoize
@@ -67,3 +68,41 @@ export function getDependencyVersion(dependencyName: string): string | null {
} }
return null; return null;
} }
/**
* Resolve a usable version string for checks (eg. semver.satisfies).
* Strategy:
* - prefer installed package.json version (getDependencyVersion)
* - fall back to declared version in project package.json (dependencies/devDependencies)
* - if declared is a common dist-tag (alpha|beta|rc|next) return a 9.x prerelease
*/
export function getResolvedDependencyVersionForCheck(dependencyName: string, target: string): string | null {
// try installed
const installed = getDependencyVersion(dependencyName);
if (installed) {
return installed;
}
// try declared in project package.json
const pkg = getPackageJson();
const declared = (pkg.dependencies && pkg.dependencies[dependencyName]) || (pkg.devDependencies && pkg.devDependencies[dependencyName]);
if (!declared) {
return null;
}
// if declared already satisfies semver check, use it
try {
if (satisfies(declared, `>=${target}`)) {
return declared;
}
} catch (e) {
// ignore parse errors
}
// common dist-tags -> treat as prerelease of 9.x for the purpose of >=9 checks
if (/^(alpha|beta|rc|next)$/.test(String(declared))) {
return `${target}-0`;
}
return declared ?? null;
}