Repository navigation
RFC: --compiled — release builds compiled to Swift and Kotlin, with no JavaScript runtime
#59
NathanWalker
started this conversation in
1. Ideas & Discussions
Replies: 1 comment
|
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
RFC:
--compiled— release builds compiled to Swift and Kotlin, with no JavaScript runtime9.2.0-alpha.0ofnativescript,@nativescript/coreand@nativescript/compileris on npm (dist-tagalpha)packages/compileronfeat/native-kit· CLI:feat/native-release· issue intake: NativeScript#11546ns:swift), rfcs#58 (ns:native)Summary
NativeScript has never offered a way to ship without a JavaScript runtime. This RFC proposes one, as an option beside the JavaScript release:
ns runandns debugstill run on the JavaScript runtime with HMR.--compiledchanges only how a release is built.@nativescript/coreitself, to Swift and Kotlin. The output is an ordinary Xcode or Gradle project inplatforms/compiled/<platform>. No JavaScript engine ships.@nativescript/compiler.release: { compiled: true }innativescript.config.tsmakes it the default;--no-compiledbuilds a JavaScript release instead.Motivation
Size, memory and launch. A JavaScript release carries an engine, the framework's JavaScript and a bundle. Measured on our own apps against their own JavaScript releases:
.ipaTyped code runs at native speed. On iOS, V8 runs without a JIT. Against it, typed method calls and field writes ran about 100 times faster compiled, and a numeric n-body simulation 11 times faster.
Teams that can't ship a JavaScript runtime. Some organizations require native code they can audit, or simply want "a native app" with no qualifiers. Today they leave NativeScript to get it. Compiled output is ordinary Swift 6 and Kotlin that opens in Xcode and Android Studio, and crash reports and debuggers point at the TypeScript lines.
NativeScript is unusually well placed to do this. Compiling a dynamic language to native code is hard because the compiler must guess what each call means. NativeScript removes most of the guessing:
@nativescript/types-iosandtypes-androidare generated from the SDKs. A call toUIImage.systemImageNamedWithConfiguration(...)becomes the Swift call, with no bridge to emulate.*.ios.tsand*.android.tsdrive UIKit and Android views directly, so the compiled app uses the real views, laid out by core's own code. That's why compiled galleries match their JavaScript builds pixel for pixel.Guide-level explanation
Building
Or make it the default for release builds:
releaseis also the compiler's options object (pluginReplacements,pluginSources,stubPlugins,allowUnimplementedProperties). A platform'sreleasemerges over the top-level one.When something can't compile
The build stops with the TypeScript file and line and the reason. For example,
evalin a reachable module, or a core property the kit doesn't implement yet. There are escape hatches with warnings (allowUnimplementedProperties,--allow-core-mismatch,--allow-untested-frameworks), and--no-compiledbuilds the JavaScript release.Reporting a failure
When a compiled build fails, the CLI saves a report and prints one line:
ns compiled report <platform>. It collects versions, platform, toolchain and the diagnostic with paths and secrets redacted, and shows all of it before anything is sent; the default answer is no. Each report carries a signature of the error. An existing issue with that signature gets a 👍; otherwise a labelled issue is opened through an issue form (#11546). Each report becomes a differential test case, and the fix lands once in the compiler.What
strictbuys youTypeScript's types are erased: a parameter typed
Personcan receiveundefinedat run time. The compiler follows one rule: a type is a contract only where TypeScript can prove it.Both behave as JavaScript does. The strict form is smaller, faster and reads like hand-written Swift. Core and plugins compile under their own settings, so an app's
strictonly affects the app's code.What won't work
Some JavaScript can't exist without an engine, and is refused at build time:
eval,new Function, downloaded code and over-the-air JavaScript updates;Proxy,Reflect,with,SharedArrayBuffer,FinalizationRegistry;Apps that depend on these keep the JavaScript release.
Reference-level details
Pipeline
anystays a dynamic value.@NativeClasssubclasses become real native subclasses.Runtime model
@UiThread/@AnyThread.Application.uncaughtErrorEventand leaves that region empty. Calling an API newer than the running OS throws a catchableTypeError. A CI gate fails any change that adds a new crash site ("trap") to the kit.any,as, native returns) are held loosely and converted where used, checked only where JavaScript would throw.Plugins and npm packages
Plugins compile from their TypeScript source, resolved from the published package or its repository at the published version (including relocated monorepos and unpublished tags). Mixed Swift and Objective-C plugins, header-only modules and vendored static pods are linked. Three kinds can't compile as they are, and
release.pluginReplacementsmaps the package to a typed module in the app:nativescript-ui-listviewmomentargumentsoverloading have no static formstacktrace-jsKeeping in step with core
-Osize), builds the kit with warnings as errors, lints Kotlin's thread annotations, and builds and launches an app on an Android emulator and the two newest iOS simulators.@nativescript/compiler; the compiler refuses a core version it wasn't generated from.Where it stands
Drawbacks
ns compiled reportdoes, but triage still gets a new question.error.stackshows native frames. Others are work in progress (see above).Alternatives considered
Adoption plan
9.2.0-alpha.1: announce to core contributors and selected community members; merge the docs and the issue form; labelcompiledissues.feat/native-kitintomain, with the Compiler workflow required on core PRs.Unresolved questions
packages/compilerin the core monorepo (current, so the kit regenerates with every core change), or its own repository?@angular/coreand friends from source, as rxjs and NgRx already are?strictness.json), and shouldns createtemplates default tostrict?ns:nativespecifiers from rfcs#58? In a compiled build every native reference is already static, so they could map one-to-one to Swift module imports and Kotlin packages.All reactions