From 248a85f6e08b570cf19f09d146dcb0227ce089f2 Mon Sep 17 00:00:00 2001 From: Nathan Walker Date: Sat, 19 Jul 2025 01:18:12 -0700 Subject: [PATCH] chore: cleanup --- .../core/application/application-common.ts | 7 +- .../core/application/application.android.ts | 7 +- packages/core/css/css-tree-parser.ts | 4 +- .../file-system/file-system-access.ios.ts | 25 +- packages/core/fps-meter/fps-native.android.ts | 3 +- packages/core/fps-meter/fps-native.ios.ts | 4 +- packages/core/global-types.d.ts | 2 +- packages/core/globals/global-utils.ts | 74 +++ packages/core/globals/index.ts | 573 ++++++++---------- packages/core/profiling/index.ts | 4 - packages/core/ui/core/view/index.android.ts | 4 +- packages/core/ui/styling/style-scope.ts | 19 +- packages/core/utils/native-helper.ios.ts | 3 - packages/core/vitest.setup.ts | 5 + packages/webpack5/src/configuration/base.ts | 122 ++-- 15 files changed, 439 insertions(+), 417 deletions(-) create mode 100644 packages/core/globals/global-utils.ts diff --git a/packages/core/application/application-common.ts b/packages/core/application/application-common.ts index 439a34931..960932081 100644 --- a/packages/core/application/application-common.ts +++ b/packages/core/application/application-common.ts @@ -14,6 +14,7 @@ import type { AndroidApplication as AndroidApplicationType, iOSApplication as iO import type { ApplicationEventData, CssChangedEventData, DiscardedErrorEventData, FontScaleChangedEventData, InitRootViewEventData, LaunchEventData, LoadAppCSSEventData, NativeScriptError, OrientationChangedEventData, SystemAppearanceChangedEventData, UnhandledErrorEventData } from './application-interfaces'; import { readyInitAccessibilityCssHelper, readyInitFontScale } from '../accessibility/accessibility-common'; import { getAppMainEntry, isAppInBackground, setAppInBackground, setAppMainEntry } from './helpers-common'; +import { getNativeScriptGlobals } from '../globals/global-utils'; // prettier-ignore const ORIENTATION_CSS_CLASSES = [ @@ -28,7 +29,7 @@ const SYSTEM_APPEARANCE_CSS_CLASSES = [ `${CSSUtils.CLASS_PREFIX}${CoreTypes.SystemAppearance.dark}`, ]; -const globalEvents = global.NativeScriptGlobals.events; +const globalEvents = getNativeScriptGlobals().events; // helper interface to correctly type Application event handlers interface ApplicationEvents { @@ -161,7 +162,7 @@ export class ApplicationCommon { * @internal - should not be constructed by the user. */ constructor() { - global.NativeScriptGlobals.appInstanceReady = true; + getNativeScriptGlobals().appInstanceReady = true; global.__onUncaughtError = (error: NativeScriptError) => { this.notify({ @@ -507,7 +508,7 @@ export class ApplicationCommon { } hasLaunched(): boolean { - return global.NativeScriptGlobals && global.NativeScriptGlobals.launched; + return getNativeScriptGlobals().launched; } private _systemAppearance: 'dark' | 'light' | null; diff --git a/packages/core/application/application.android.ts b/packages/core/application/application.android.ts index 29a1a530d..fb0367171 100644 --- a/packages/core/application/application.android.ts +++ b/packages/core/application/application.android.ts @@ -42,6 +42,7 @@ import { } from '../accessibility/accessibility-common'; import { androidGetForegroundActivity, androidGetStartActivity, androidPendingReceiverRegistrations, androidRegisterBroadcastReceiver, androidRegisteredReceivers, androidSetForegroundActivity, androidSetStartActivity, androidUnregisterBroadcastReceiver, applyContentDescription } from './helpers'; import { getImageFetcher, getNativeApp, getRootView, initImageCache, setA11yUpdatePropertiesCallback, setApplicationPropertiesCallback, setAppMainEntry, setNativeApp, setRootView, setToggleApplicationEventListenersCallback } from './helpers-common'; +import { getNativeScriptGlobals } from '../globals/global-utils'; declare class NativeScriptLifecycleCallbacks extends android.app.Application.ActivityLifecycleCallbacks {} @@ -1364,9 +1365,9 @@ function onLiveSync(args): void { } } -global.NativeScriptGlobals.events.on('livesync', onLiveSync); +getNativeScriptGlobals().events.on('livesync', onLiveSync); -global.NativeScriptGlobals.addEventWiring(() => { +getNativeScriptGlobals().addEventWiring(() => { Application.android.on('activityStarted', (args: any) => { if (!getImageFetcher()) { initImageCache(args.activity); @@ -1376,7 +1377,7 @@ global.NativeScriptGlobals.addEventWiring(() => { }); }); -global.NativeScriptGlobals.addEventWiring(() => { +getNativeScriptGlobals().addEventWiring(() => { Application.android.on('activityStopped', (args) => { if (getImageFetcher()) { getImageFetcher().closeCache(); diff --git a/packages/core/css/css-tree-parser.ts b/packages/core/css/css-tree-parser.ts index 287c1c9d4..57d54abae 100644 --- a/packages/core/css/css-tree-parser.ts +++ b/packages/core/css/css-tree-parser.ts @@ -1,4 +1,4 @@ -import { parse } from 'css-tree'; +import * as cssTree from 'css-tree'; function mapSelectors(selector: string): string[] { if (!selector) { @@ -129,7 +129,7 @@ function transformAst(node, css, type = null) { export function cssTreeParse(css, source): any { const errors = []; - const ast = parse(css, { + const ast = cssTree.parse(css, { parseValue: false, parseAtrulePrelude: false, parseRulePrelude: false, diff --git a/packages/core/file-system/file-system-access.ios.ts b/packages/core/file-system/file-system-access.ios.ts index 7e23da551..05925eb8b 100644 --- a/packages/core/file-system/file-system-access.ios.ts +++ b/packages/core/file-system/file-system-access.ios.ts @@ -1,6 +1,5 @@ import { encoding as textEncoding } from '../text'; -import { ios as iosUtils } from '../utils'; -import { getFileExtension } from '../utils/utils-shared'; +import { iOSNativeHelper } from '../utils'; // TODO: Implement all the APIs receiving callback using async blocks // TODO: Check whether we need try/catch blocks for the iOS implementation @@ -258,7 +257,7 @@ export class FileSystemAccess { } public getCurrentAppPath(): string { - return iosUtils.getCurrentAppPath(); + return iOSNativeHelper.getCurrentAppPath(); } public copy = this.copySync.bind(this); @@ -612,8 +611,24 @@ export class FileSystemAccess { return url.path; } + // TODO: This method is the same as in the iOS implementation. + // Make it in a separate file / module so it can be reused from both implementations. public getFileExtension(path: string): string { - return getFileExtension(path); + // TODO [For Panata]: The definitions currently specify "any" as a return value of this method + //const nsString = Foundation.NSString.stringWithString(path); + //const extension = nsString.pathExtension(); + + //if (extension && extension.length > 0) { + // extension = extension.concat(".", extension); + //} + + //return extension; + const dotIndex = path.lastIndexOf('.'); + if (dotIndex && dotIndex >= 0 && dotIndex < path.length) { + return path.substring(dotIndex); + } + + return ''; } private deleteEntity(path: string, onError?: (error: any) => any) { @@ -685,7 +700,7 @@ export class FileSystemAccess { } public joinPaths(paths: string[]): string { - return iosUtils.joinPaths(...paths); + return iOSNativeHelper.joinPaths(...paths); } } diff --git a/packages/core/fps-meter/fps-native.android.ts b/packages/core/fps-meter/fps-native.android.ts index 0dc0415b5..7baa12a3b 100644 --- a/packages/core/fps-meter/fps-native.android.ts +++ b/packages/core/fps-meter/fps-native.android.ts @@ -1,7 +1,6 @@ -import * as definition from './fps-native'; import { SDK_VERSION } from '../utils/constants'; -export class FPSCallback implements definition.FPSCallback { +export class FPSCallback { private impl: android.view.Choreographer.FrameCallback | ((nanos: number) => void); private onFrame: (currentTimeMillis: number) => void; diff --git a/packages/core/fps-meter/fps-native.ios.ts b/packages/core/fps-meter/fps-native.ios.ts index c546f7475..3d4252e26 100644 --- a/packages/core/fps-meter/fps-native.ios.ts +++ b/packages/core/fps-meter/fps-native.ios.ts @@ -1,5 +1,3 @@ -import * as definition from './fps-native'; - @NativeClass class FrameHandlerImpl extends NSObject { private _owner: WeakRef; @@ -23,7 +21,7 @@ class FrameHandlerImpl extends NSObject { }; } -export class FPSCallback implements definition.FPSCallback { +export class FPSCallback { public running: boolean; private onFrame: Function; private displayLink: CADisplayLink; diff --git a/packages/core/global-types.d.ts b/packages/core/global-types.d.ts index a116b5237..b53ac6ba8 100644 --- a/packages/core/global-types.d.ts +++ b/packages/core/global-types.d.ts @@ -26,7 +26,7 @@ declare interface NativeScriptError extends Error { //Augment the NodeJS global type with our own extensions declare module globalThis { - var NativeScriptHasInitGlobal: boolean; + var NativeScriptHasPolyfilled: boolean; var NativeScriptGlobals: { /** * Global framework event handling diff --git a/packages/core/globals/global-utils.ts b/packages/core/globals/global-utils.ts new file mode 100644 index 000000000..cd47208d3 --- /dev/null +++ b/packages/core/globals/global-utils.ts @@ -0,0 +1,74 @@ +import { Observable } from '../data/observable'; +import { trace as profilingTrace, time, uptime, level as profilingLevel } from '../profiling'; +console.log('here in globals/global-utils!'); + +/** + * Manages internal framework global state + */ +export class NativeScriptGlobalState { + events: Observable; + launched = false; + // used by various classes to setup callbacks to wire up global app event handling when the app instance is ready + appEventWiring: Array; + private _appInstanceReady = false; + private _setLaunched: () => void; + constructor() { + // console.log('creating NativeScriptGlobals...') + this.events = new Observable(); + this._setLaunched = this._setLaunchedFn.bind(this); + this.events.on('launch', this._setLaunched); + if (profilingLevel() > 0) { + this.events.on('displayed', () => { + const duration = uptime(); + const end = time(); + const start = end - duration; + profilingTrace(`Displayed in ${duration.toFixed(2)}ms`, start, end); + }); + } + } + + get appInstanceReady() { + return this._appInstanceReady; + } + + set appInstanceReady(value: boolean) { + this._appInstanceReady = value; + // app instance ready, wire up any app events waiting in startup queue + if (this.appEventWiring && this.appEventWiring.length) { + for (const callback of this.appEventWiring) { + callback(); + } + // cleanup + this.appEventWiring = null; + } + } + + /** + * Ability for classes to initialize app event handling early even before the app instance is ready during boot cycle avoiding boot race conditions + * @param callback wire up any global event handling inside the callback + */ + addEventWiring(callback: () => void) { + if (this._appInstanceReady) { + callback(); + } else { + if (!this.appEventWiring) { + this.appEventWiring = []; + } + this.appEventWiring.push(callback); + } + } + + private _setLaunchedFn() { + // console.log('NativeScriptGlobals launch fired!'); + this.launched = true; + this.events.off('launch', this._setLaunched); + this._setLaunched = null; + } +} +export function getNativeScriptGlobals() { + if (!global.NativeScriptGlobals) { + // init global state handler + global.NativeScriptGlobals = new NativeScriptGlobalState(); + } + return global.NativeScriptGlobals; +} diff --git a/packages/core/globals/index.ts b/packages/core/globals/index.ts index 86db819ab..56ea35c8b 100644 --- a/packages/core/globals/index.ts +++ b/packages/core/globals/index.ts @@ -1,6 +1,4 @@ import tslib from 'tslib'; -import { Observable } from '../data/observable'; -import { trace as profilingTrace, time, uptime, level as profilingLevel } from '../profiling'; import * as timer from '../timer'; import * as animationFrame from '../animation-frame'; import * as mediaQueryList from '../media-query-list'; @@ -50,6 +48,230 @@ interface ExtensionMap { [originalFileExtension: string]: string; } +// ts-helpers +// Required by V8 snapshot generator +if (!global.__extends) { + global.__extends = function (d, b) { + for (const p in b) { + if (b.hasOwnProperty(p)) { + d[p] = b[p]; + } + } + function __() { + this.constructor = d; + } + d.prototype = b === null ? Object.create(b) : ((__.prototype = b.prototype), new __()); + }; +} + +// Bind the tslib helpers to global scope. +// This is needed when we don't use importHelpers, which +// breaks extending native-classes +for (const fnName of Object.getOwnPropertyNames(tslib)) { + if (typeof tslib[fnName] !== 'function') { + continue; + } + + if (fnName in global) { + // Don't override globals that are already defined (ex. __extends) + continue; + } + + global[fnName] = tslib[fnName]; +} + +// module helpers +const defaultExtensionMap: ExtensionMap = { + '.js': '.js', + '.ts': '.js', + '.kt': '.js', + '.css': '.css', + '.scss': '.css', + '.less': '.css', + '.sass': '.css', + '.xml': '.xml', +}; + +// Cast to because moduleResolvers is read-only in definitions +global.moduleResolvers = [global.require]; + +global.registerModule = function (name: string, loader: ModuleLoader): void { + modules.set(name, { loader, moduleId: name }); +}; + +global._unregisterModule = function _unregisterModule(name: string): void { + modules.delete(name); +}; + +global._isModuleLoadedForUI = function _isModuleLoadedForUI(moduleName: string): boolean { + return modulesLoadedForUI.has(moduleName); +}; + +global.registerWebpackModules = function registerWebpackModules(context: Context, extensionMap: ExtensionMap = {}) { + context.keys().forEach((moduleId) => { + const extDotIndex = moduleId.lastIndexOf('.'); + const base = moduleId.substring(0, extDotIndex); + const originalExt = moduleId.substring(extDotIndex); + const registerExt = extensionMap[originalExt] || defaultExtensionMap[originalExt] || originalExt; + + // We prefer source files for webpack scenarios before compilation leftovers, + // e. g. if we get a .js and .ts for the same module, the .js is probably the compiled version of the .ts file, + // so we register the .ts with higher priority, similar is the case with us preferring the .scss to .css + const isSourceFile = originalExt !== registerExt; + const registerName = base + registerExt; + + const registerWithName = (nickName: string) => { + modules.set(nickName, { + moduleId, + loader: () => { + return context(moduleId); + }, + }); + }; + + if (registerName.startsWith('./') && registerName.endsWith('.js')) { + const jsNickNames = [ + // This is extremely short version like "main-page" that was promoted to be used with global.registerModule("module-name", loaderFunc); + registerName.substring(2, registerName.length - 5), + // This is for supporting module names like "./main/main-page" + registerName.substring(0, registerName.length - 3), + // This is for supporting module names like "main/main-page.js" + registerName.substring(2), + ]; + + jsNickNames.forEach((jsNickName) => { + if (isSourceFile || !global.moduleExists(jsNickName)) { + registerWithName(jsNickName); + } + }); + } else if (registerName.startsWith('./')) { + const moduleNickNames = [ + // This is for supporting module names like "main/main-page.xml" + registerName.substring(2), + ]; + + moduleNickNames.forEach((moduleNickName) => { + if (!global.moduleExists(moduleNickName)) { + registerWithName(moduleNickName); + } + }); + } + + if (isSourceFile || !global.moduleExists(registerName)) { + registerWithName(registerName); + } + }); +}; + +global.moduleExists = function moduleExists(name: string): boolean { + return modules.has(name); +}; + +global.getRegisteredModules = function getRegisteredModules(): string[] { + return Array.from(modules.keys()); +}; + +/** + * Polyfills + */ +// This method iterates all the keys in the source exports object and copies them to the destination exports one. +// Note: the method will not check for naming collisions and will override any already existing entries in the destination exports. +global.moduleMerge = function (sourceExports: any, destExports: any) { + for (const key in sourceExports) { + destExports[key] = sourceExports[key]; + } +}; + +global.zonedCallback = function (callback: Function): Function { + if (global.zone) { + // Zone v0.5.* style callback wrapping + return global.zone.bind(callback); + } + if (global.Zone) { + // Zone v0.6.* style callback wrapping + return global.Zone.current.wrap(callback); + } else { + return callback; + } +}; + +global.System = { + import(path) { + return new Promise((resolve, reject) => { + try { + resolve(global.require(path)); + } catch (e) { + reject(e); + } + }); + }, +}; + +// Custom decorators + +global.Deprecated = function (target: Object, key?: string | symbol, descriptor?: any) { + if (descriptor) { + const originalMethod = descriptor.value; + + descriptor.value = function (...args: any[]) { + console.log(`${key.toString()} is deprecated`); + + return originalMethod.apply(this, args); + }; + + return descriptor; + } else { + console.log(`${(target && (target).name) || target} is deprecated`); + + return target; + } +}; + +global.Experimental = function (target: Object, key?: string | symbol, descriptor?: any) { + if (descriptor) { + const originalMethod = descriptor.value; + + descriptor.value = function (...args: any[]) { + console.log(`${key.toString()} is experimental`); + + return originalMethod.apply(this, args); + }; + + return descriptor; + } else { + console.log(`${(target && (target).name) || target} is experimental`); + + return target; + } +}; +const modules: Map = new Map(); +const modulesLoadedForUI = new Set(); + +global.loadModule = function loadModule(name: string, isUIModule = false): any { + const moduleInfo = modules.get(name); + if (moduleInfo) { + if (isUIModule) { + modulesLoadedForUI.add(moduleInfo.moduleId); + } + + const result = moduleInfo.loader(name); + + if (result.enableAutoAccept) { + result.enableAutoAccept(); + } + + return result; + } + + for (const resolver of global.moduleResolvers) { + const result = resolver(name); + if (result) { + modules.set(name, { moduleId: name, loader: () => result }); + + return result; + } + } +}; function registerOnGlobalContext(moduleName: string, exportName: string): void { Object.defineProperty(global, exportName, { get: function () { @@ -70,70 +292,6 @@ function registerOnGlobalContext(moduleName: string, exportName: string): void { }); } -/** - * Manages internal framework global state - */ -export class NativeScriptGlobalState { - events: Observable; - launched = false; - // used by various classes to setup callbacks to wire up global app event handling when the app instance is ready - appEventWiring: Array; - private _appInstanceReady = false; - private _setLaunched: () => void; - constructor() { - // console.log('creating NativeScriptGlobals...') - this.events = new Observable(); - this._setLaunched = this._setLaunchedFn.bind(this); - this.events.on('launch', this._setLaunched); - if (profilingLevel() > 0) { - this.events.on('displayed', () => { - const duration = uptime(); - const end = time(); - const start = end - duration; - profilingTrace(`Displayed in ${duration.toFixed(2)}ms`, start, end); - }); - } - } - - get appInstanceReady() { - return this._appInstanceReady; - } - - set appInstanceReady(value: boolean) { - this._appInstanceReady = value; - // app instance ready, wire up any app events waiting in startup queue - if (this.appEventWiring && this.appEventWiring.length) { - for (const callback of this.appEventWiring) { - callback(); - } - // cleanup - this.appEventWiring = null; - } - } - - /** - * Ability for classes to initialize app event handling early even before the app instance is ready during boot cycle avoiding boot race conditions - * @param callback wire up any global event handling inside the callback - */ - addEventWiring(callback: () => void) { - if (this._appInstanceReady) { - callback(); - } else { - if (!this.appEventWiring) { - this.appEventWiring = []; - } - this.appEventWiring.push(callback); - } - } - - private _setLaunchedFn() { - // console.log('NativeScriptGlobals launch fired!'); - this.launched = true; - this.events.off('launch', this._setLaunched); - this._setLaunched = null; - } -} - export function installPolyfills(moduleName: string, exportNames: string[]) { if (global.__snapshot) { const loadedModule = global.loadModule(moduleName); @@ -143,270 +301,41 @@ export function installPolyfills(moduleName: string, exportNames: string[]) { } } -export function initGlobal() { - if (!global.NativeScriptHasInitGlobal) { - global.NativeScriptHasInitGlobal = true; - // init global state handler - global.NativeScriptGlobals = new NativeScriptGlobalState(); +if (!global.NativeScriptHasPolyfilled) { + global.NativeScriptHasPolyfilled = true; + console.log('Installing polyfills...'); + // DOM api polyfills + global.registerModule('timer', () => timer); + installPolyfills('timer', ['setTimeout', 'clearTimeout', 'setInterval', 'clearInterval']); - // ts-helpers - // Required by V8 snapshot generator - if (!global.__extends) { - global.__extends = function (d, b) { - for (const p in b) { - if (b.hasOwnProperty(p)) { - d[p] = b[p]; - } - } - function __() { - this.constructor = d; - } - d.prototype = b === null ? Object.create(b) : ((__.prototype = b.prototype), new __()); - }; - } + global.registerModule('animation', () => animationFrame); + installPolyfills('animation', ['requestAnimationFrame', 'cancelAnimationFrame']); - // Bind the tslib helpers to global scope. - // This is needed when we don't use importHelpers, which - // breaks extending native-classes - for (const fnName of Object.getOwnPropertyNames(tslib)) { - if (typeof tslib[fnName] !== 'function') { - continue; - } + global.registerModule('media-query-list', () => mediaQueryList); + installPolyfills('media-query-list', ['matchMedia', 'MediaQueryList']); - if (fnName in global) { - // Don't override globals that are already defined (ex. __extends) - continue; - } + global.registerModule('text', () => text); + installPolyfills('text', ['TextDecoder', 'TextEncoder']); - global[fnName] = tslib[fnName]; - } + global.registerModule('xhr', () => xhrImpl); + installPolyfills('xhr', ['XMLHttpRequest', 'FormData', 'Blob', 'File', 'FileReader']); - // module helpers - const modules: Map = new Map(); - const modulesLoadedForUI = new Set(); - const defaultExtensionMap: ExtensionMap = { - '.js': '.js', - '.ts': '.js', - '.kt': '.js', - '.css': '.css', - '.scss': '.css', - '.less': '.css', - '.sass': '.css', - '.xml': '.xml', - }; + global.registerModule('fetch', () => fetchPolyfill); + installPolyfills('fetch', ['fetch', 'Headers', 'Request', 'Response']); - // Cast to because moduleResolvers is read-only in definitions - global.moduleResolvers = [global.require]; + global.registerModule('wgc', () => wgc); + installPolyfills('wgc', ['atob', 'btoa']); - global.registerModule = function (name: string, loader: ModuleLoader): void { - modules.set(name, { loader, moduleId: name }); - }; + global.registerModule('crypto', () => cryptoImpl); + installPolyfills('crypto', ['Crypto']); - global._unregisterModule = function _unregisterModule(name: string): void { - modules.delete(name); - }; + global.registerModule('subtle', () => subtleCryptoImpl); + installPolyfills('subtle-crypto', ['Subtle']); - global._isModuleLoadedForUI = function _isModuleLoadedForUI(moduleName: string): boolean { - return modulesLoadedForUI.has(moduleName); - }; + global.crypto = new global.Crypto(); - global.registerWebpackModules = function registerWebpackModules(context: Context, extensionMap: ExtensionMap = {}) { - context.keys().forEach((moduleId) => { - const extDotIndex = moduleId.lastIndexOf('.'); - const base = moduleId.substr(0, extDotIndex); - const originalExt = moduleId.substr(extDotIndex); - const registerExt = extensionMap[originalExt] || defaultExtensionMap[originalExt] || originalExt; - - // We prefer source files for webpack scenarios before compilation leftovers, - // e. g. if we get a .js and .ts for the same module, the .js is probably the compiled version of the .ts file, - // so we register the .ts with higher priority, similar is the case with us preferring the .scss to .css - const isSourceFile = originalExt !== registerExt; - const registerName = base + registerExt; - - const registerWithName = (nickName: string) => { - modules.set(nickName, { - moduleId, - loader: () => { - return context(moduleId); - }, - }); - }; - - if (registerName.startsWith('./') && registerName.endsWith('.js')) { - const jsNickNames = [ - // This is extremely short version like "main-page" that was promoted to be used with global.registerModule("module-name", loaderFunc); - registerName.substr(2, registerName.length - 5), - // This is for supporting module names like "./main/main-page" - registerName.substr(0, registerName.length - 3), - // This is for supporting module names like "main/main-page.js" - registerName.substr(2), - ]; - - jsNickNames.forEach((jsNickName) => { - if (isSourceFile || !global.moduleExists(jsNickName)) { - registerWithName(jsNickName); - } - }); - } else if (registerName.startsWith('./')) { - const moduleNickNames = [ - // This is for supporting module names like "main/main-page.xml" - registerName.substr(2), - ]; - - moduleNickNames.forEach((moduleNickName) => { - if (!global.moduleExists(moduleNickName)) { - registerWithName(moduleNickName); - } - }); - } - - if (isSourceFile || !global.moduleExists(registerName)) { - registerWithName(registerName); - } - }); - }; - - global.moduleExists = function moduleExists(name: string): boolean { - return modules.has(name); - }; - - global.loadModule = function loadModule(name: string, isUIModule = false): any { - const moduleInfo = modules.get(name); - if (moduleInfo) { - if (isUIModule) { - modulesLoadedForUI.add(moduleInfo.moduleId); - } - - const result = moduleInfo.loader(name); - - if (result.enableAutoAccept) { - result.enableAutoAccept(); - } - - return result; - } - - for (const resolver of global.moduleResolvers) { - const result = resolver(name); - if (result) { - modules.set(name, { moduleId: name, loader: () => result }); - - return result; - } - } - }; - - global.getRegisteredModules = function getRegisteredModules(): string[] { - return Array.from(modules.keys()); - }; - - /** - * Polyfills - */ - // This method iterates all the keys in the source exports object and copies them to the destination exports one. - // Note: the method will not check for naming collisions and will override any already existing entries in the destination exports. - global.moduleMerge = function (sourceExports: any, destExports: any) { - for (const key in sourceExports) { - destExports[key] = sourceExports[key]; - } - }; - - global.zonedCallback = function (callback: Function): Function { - if (global.zone) { - // Zone v0.5.* style callback wrapping - return global.zone.bind(callback); - } - if (global.Zone) { - // Zone v0.6.* style callback wrapping - return global.Zone.current.wrap(callback); - } else { - return callback; - } - }; - - global.System = { - import(path) { - return new Promise((resolve, reject) => { - try { - resolve(global.require(path)); - } catch (e) { - reject(e); - } - }); - }, - }; - - // DOM api polyfills - global.registerModule('timer', () => timer); - installPolyfills('timer', ['setTimeout', 'clearTimeout', 'setInterval', 'clearInterval']); - - global.registerModule('animation', () => animationFrame); - installPolyfills('animation', ['requestAnimationFrame', 'cancelAnimationFrame']); - - global.registerModule('media-query-list', () => mediaQueryList); - installPolyfills('media-query-list', ['matchMedia', 'MediaQueryList']); - - global.registerModule('text', () => text); - installPolyfills('text', ['TextDecoder', 'TextEncoder']); - - global.registerModule('xhr', () => xhrImpl); - installPolyfills('xhr', ['XMLHttpRequest', 'FormData', 'Blob', 'File', 'FileReader']); - - global.registerModule('fetch', () => fetchPolyfill); - installPolyfills('fetch', ['fetch', 'Headers', 'Request', 'Response']); - - global.registerModule('wgc', () => wgc); - installPolyfills('wgc', ['atob', 'btoa']); - - global.registerModule('crypto', () => cryptoImpl); - installPolyfills('crypto', ['Crypto']); - - global.registerModule('subtle', () => subtleCryptoImpl); - installPolyfills('subtle-crypto', ['Subtle']); - - global.crypto = new global.Crypto(); - - // global.registerModule('abortcontroller', () => require('../abortcontroller')); - // installPolyfills('abortcontroller', ['AbortController', 'AbortSignal']); - - // Custom decorators - - global.Deprecated = function (target: Object, key?: string | symbol, descriptor?: any) { - if (descriptor) { - const originalMethod = descriptor.value; - - descriptor.value = function (...args: any[]) { - console.log(`${key.toString()} is deprecated`); - - return originalMethod.apply(this, args); - }; - - return descriptor; - } else { - console.log(`${(target && (target).name) || target} is deprecated`); - - return target; - } - }; - - global.Experimental = function (target: Object, key?: string | symbol, descriptor?: any) { - if (descriptor) { - const originalMethod = descriptor.value; - - descriptor.value = function (...args: any[]) { - console.log(`${key.toString()} is experimental`); - - return originalMethod.apply(this, args); - }; - - return descriptor; - } else { - console.log(`${(target && (target).name) || target} is experimental`); - - return target; - } - }; - } + // global.registerModule('abortcontroller', () => require('../abortcontroller')); + // installPolyfills('abortcontroller', ['AbortController', 'AbortSignal']); } declare const jest: any; @@ -414,11 +343,7 @@ function isTestingEnv() { return typeof jest !== 'undefined' || global.__UNIT_TEST__; } -console.log('global.NativeScriptHasInitGlobal:', global.NativeScriptHasInitGlobal); console.log('isTestingEnv():', isTestingEnv()); -if (!global.NativeScriptHasInitGlobal && !isTestingEnv()) { - initGlobal(); -} // ensure the Application instance is initialized before any other module imports it. import '../application'; diff --git a/packages/core/profiling/index.ts b/packages/core/profiling/index.ts index d0b893048..3906e9588 100644 --- a/packages/core/profiling/index.ts +++ b/packages/core/profiling/index.ts @@ -160,10 +160,6 @@ export function enable(mode: InstrumentationMode = 'counters') { } try { - // const appConfig = require('~/package.json'); - // @ts-ignore - // const appConfig = await import('~/package.json'); - console.log('profiling appConfig:', appConfig); if (appConfig && appConfig.profiling) { enable(appConfig.profiling); } diff --git a/packages/core/ui/core/view/index.android.ts b/packages/core/ui/core/view/index.android.ts index 9b8414065..5642f9c99 100644 --- a/packages/core/ui/core/view/index.android.ts +++ b/packages/core/ui/core/view/index.android.ts @@ -1,6 +1,6 @@ import type { Point, Position } from './view-interfaces'; import type { GestureTypes, GestureEventData } from '../../gestures'; - +import { getNativeScriptGlobals } from '../../../globals/global-utils'; import { ViewCommon, isEnabledProperty, originXProperty, originYProperty, isUserInteractionEnabledProperty, testIDProperty, AndroidHelper } from './view-common'; import { paddingLeftProperty, paddingTopProperty, paddingRightProperty, paddingBottomProperty } from '../../styling/style-properties'; import { Length } from '../../styling/length-shared'; @@ -143,7 +143,7 @@ function initializeDialogFragment() { }; // Fist fire application.android global event - global.NativeScriptGlobals.events.notify(args); + getNativeScriptGlobals().events.notify(args); if (args.cancel) { return; } diff --git a/packages/core/ui/styling/style-scope.ts b/packages/core/ui/styling/style-scope.ts index f1414217e..81a1616e8 100644 --- a/packages/core/ui/styling/style-scope.ts +++ b/packages/core/ui/styling/style-scope.ts @@ -1,3 +1,4 @@ +import { getNativeScriptGlobals } from '../../globals/global-utils'; import { ViewBase } from '../core/view-base'; import { View } from '../core/view'; import { _evaluateCssVariableExpression, _evaluateCssCalcExpression, isCssVariable, isCssVariableExpression, isCssCalcExpression } from '../core/properties'; @@ -23,18 +24,8 @@ import { parse as parseCss } from '../../css/lib/parse'; // @ts-ignore apps resolve this at runtime with path alias in project bundlers import appConfig from '~/package.json'; -// if (!global.__dirname) { -// global.__dirname = typeof __dirname !== 'undefined' ? __dirname : import.meta.dirname; -// } -// const appPackagePath = path.join(global.__dirname, 'package.json'); -console.log('style-scope appConfig:', appConfig); - let parser: 'rework' | 'nativescript' | 'css-tree' = 'css-tree'; try { - console.log('style-scope here??'); - // @ts-ignore - // const appConfig = require('~/package.json'); - // console.log('style-scope appConfig:', appConfig); if (appConfig) { if (appConfig.cssParser === 'rework') { parser = 'rework'; @@ -497,8 +488,8 @@ const loadCss = profile(`"style-scope".loadCss`, (cssModule: string): void => { } }); -global.NativeScriptGlobals.events.on('cssChanged', onCssChanged); -global.NativeScriptGlobals.events.on('livesync', onLiveSync); +getNativeScriptGlobals().events.on('cssChanged', onCssChanged); +getNativeScriptGlobals().events.on('livesync', onLiveSync); // Call to this method is injected in the application in: // - no-snapshot - code injected in app.ts by [bundle-config-loader](https://github.com/NativeScript/nativescript-dev-webpack/blob/9b1e34d8ef838006c9b575285c42d2304f5f02b5/bundle-config-loader.ts#L85-L92) @@ -507,7 +498,7 @@ global.NativeScriptGlobals.events.on('livesync', onLiveSync); // when the snapshot is created - there is no way to use file qualifiers or change the name of on app.css export const loadAppCSS = profile('"style-scope".loadAppCSS', (args: LoadAppCSSEventData) => { loadCss(args.cssFile, null, null); - global.NativeScriptGlobals.events.off('loadAppCss', loadAppCSS); + getNativeScriptGlobals().events.off('loadAppCss', loadAppCSS); }); if (Application.hasLaunched()) { @@ -521,7 +512,7 @@ if (Application.hasLaunched()) { null, ); } else { - global.NativeScriptGlobals.events.on('loadAppCss', loadAppCSS); + getNativeScriptGlobals().events.on('loadAppCss', loadAppCSS); } export class CssState { diff --git a/packages/core/utils/native-helper.ios.ts b/packages/core/utils/native-helper.ios.ts index 7343f2242..48f930881 100644 --- a/packages/core/utils/native-helper.ios.ts +++ b/packages/core/utils/native-helper.ios.ts @@ -95,10 +95,7 @@ function getCurrentAppPath(): string { if (!global.__dirname) { global.__dirname = typeof __dirname !== 'undefined' ? __dirname : import.meta.dirname; } - console.log('getCurrentAppPath __dirname:', __dirname); const currentDir = global.__dirname; - console.log('getCurrentAppPath global.__dirname:', global.__dirname); - console.log('getCurrentAppPath currentDir:', currentDir); const tnsModulesIndex = currentDir.indexOf('/tns_modules'); // Module not hosted in ~/tns_modules when bundled. Use current dir. diff --git a/packages/core/vitest.setup.ts b/packages/core/vitest.setup.ts index ff5a5c89c..f8e5e7e79 100644 --- a/packages/core/vitest.setup.ts +++ b/packages/core/vitest.setup.ts @@ -1,6 +1,11 @@ // @ts-nocheck import { beforeAll, afterAll, vi } from 'vitest'; +// mock to allow tests to pass while globals attempt to polyfill crypto +vi.stubGlobal('crypto', { + randomUUID: vi.fn(() => 'mock-uuid'), + // Add other necessary crypto methods as mocks +}); // Mock out global variables and platform APIs touched while unit testing global.__UNIT_TEST__ = true; global.__DEV__ = true; diff --git a/packages/webpack5/src/configuration/base.ts b/packages/webpack5/src/configuration/base.ts index 99caeb940..1935d35ba 100644 --- a/packages/webpack5/src/configuration/base.ts +++ b/packages/webpack5/src/configuration/base.ts @@ -127,25 +127,38 @@ export default function (config: Config, env: IWebpackEnv = _env): Config { .add('@nativescript/core/inspector_modules'); }); - // config.merge({ - // experiments: { - // // enable ES module syntax (import/exports) - // outputModule: true, - // }, - // }); + if (env.commonjs) { + // CommonJS output + config.output + .path(outputPath) + .pathinfo(false) + .publicPath('') + .libraryTarget('commonjs') + .globalObject('global') + .set('clean', true); + if (env === null || env === void 0 ? void 0 : env.uniqueBundle) { + config.output.filename(`[name].${env.uniqueBundle}.js`); + } + } else { + // ESM output + config.merge({ + experiments: { + // enable ES module syntax (import/exports) + outputModule: true, + }, + }); - config.output - .path(outputPath) - .pathinfo(false) - .publicPath('') - // .set('module', true) - // .libraryTarget('module') - .libraryTarget('commonjs') - .globalObject('global') - .set('clean', true); - - if (env?.uniqueBundle) { - config.output.filename(`[name].${env.uniqueBundle}.js`); + config.output + .path(outputPath) + .pathinfo(false) + .publicPath('file:///app/') + .set('module', true) + .libraryTarget('module') + .globalObject('global') + .set('clean', true); + if (env === null || env === void 0 ? void 0 : env.uniqueBundle) { + config.output.filename(`[name].${env.uniqueBundle}.mjs`); + } } config.watchOptions({ @@ -187,29 +200,47 @@ export default function (config: Config, env: IWebpackEnv = _env): Config { config.optimization.runtimeChunk('single'); - config.optimization.splitChunks({ - // chunks: 'all', - cacheGroups: { - // globals: { - // test: (module) => - // module.resource && - // /[\\/]@nativescript[\\/]core[\\/]globals[\\/]index\.(mjs|js|ts)$/.test( - // module.resource, - // ), - // name: 'globals', - // enforce: true, // ignore size/min-chunk thresholds - // chunks: 'all', - // priority: 30, - // }, - defaultVendor: { - test: /[\\/]node_modules[\\/]/, - priority: -10, - name: 'vendor', - // enforce: true, - chunks: 'all', + if (env.commonjs) { + // Set up CommonJS output + config.optimization.splitChunks({ + cacheGroups: { + defaultVendor: { + test: /[\\/]node_modules[\\/]/, + priority: -10, + name: 'vendor', + chunks: 'all', + }, }, - }, - }); + }); + } else { + // Set up ESM output + // NOTE: this fixes all worker bundling issues + // however it causes issues with angular lazy loading. + // TODO: still need to investigate the right combination of webpack settings there + // TODO: test if standalone lazy loaded routes work, maybe it's just with loadChildren modules? + config.output.chunkFilename('[name].mjs'); + + // now re‑add exactly what you want: + config.optimization.splitChunks({ + // only split out vendor from the main bundle… + chunks: 'initial', + cacheGroups: { + // no “default” group + default: false, + + // only pull node_modules into vendor.js from the *initial* chunk + vendor: { + test: /[\\/]node_modules[\\/]/, + name: 'vendor', + chunks: 'initial', + priority: -10, + reuseExistingChunk: true, + }, + }, + }); + + config.optimization.set('moduleIds', 'named').set('chunkIds', 'named'); + } // look for loaders in // - node_modules/@nativescript/webpack/dist/loaders @@ -444,17 +475,6 @@ export default function (config: Config, env: IWebpackEnv = _env): Config { // } // ]) - // config.plugin('globals-shim').use(BannerPlugin, [ - // { - // // commonjs style - // banner: `require("./globals");`, - // // ESM style - // // banner: `import "./globals";`, - // raw: true, - // entryOnly: true, - // }, - // ]); - config.plugin('PlatformSuffixPlugin').use(PlatformSuffixPlugin, [ { extensions: platform === 'visionos' ? [platform, 'ios'] : [platform],