The highly expected 6.0 release of NativeScript is dangerously close! Two of the biggest changes in this release are the AndroidX support and the bundle (webpack-only) workflow. Both of them may require changes in existing projects. Follow this blog post to find out how to migrate your application code to NativeScript 6.0.
tns migrate
is a new CLI command aiming to help with the NativeScript 6.0 migration process. Executing this command in an existing project will update:
Update your NativeScript CLI and execute the migration command in your project:
npm install --global nativescript@latest
tns migrate
Then, run the application with the familiar command:
tns run
If you experience problems during your development workflow, you might need to make changes to your application code to make it compatible with NativeScript 6.0. Read the rest of the article to find out how. If your problem is not mentioned, please open an issue on Github.
NativeScript 6.0 supports the Android extension libraries (AndroidX). The Android Support Library is no longer supported. Any application and plugin code relying on the support library must start using AndroidX instead. To learn how to migrate, follow the dedicated blog post.
The NativeScript CLI hooks allow you to customize the execution of a CLI command. They are widely used in NativeScript plugins. The hooks API is changed in version 6.0. You might be using an outdated plugin if your build is failing with an error such as:
Cannot read property X of undefined.
To learn how to update an outdated hook refer to this article.
Prior to version 6.0, NativeScript used to support two ways of building your application:
tns build
- The Legacy Workflow, which copies the full content of the source code directory (src/) to the built application;
tns build --bundle
- The Bundle Workflow, which relies on webpack to bundle the source code directory (src/) into a few output files.
NativeScript 6.0 supports only the bundle workflow. When updating your project, you need to make sure it can be built with webpack. The NativeScript team put a lot of effort to close the gap between the legacy workflow and the bundle workflow. However, the two approaches are quite different and their behavior cannot be completely unified. Below you will find the most common differences between them with migration steps for each one.
All
The following code tries to load a local file - /assets/shipping.json.
export class CartService {
...
getShippingPrices() {
return this.http.get('/assets/shipping.json');
}
}
All files from the source directory are part of the built application. That's why the /assets/shipping.json file will be available and loaded successfully.
Webpack only compiles files that are explicitly required in the source code or included by a webpack plugin. With the default setup /assets/shipping.json won't be in the built application and the request will fail.
Configure the CopyWebpackPlugin
to move the static resources from your project to the built application.
webpack.config.js
module.exports = env => {
…
const config = {
...
plugins: [
...
new CopyWebpackPlugin([
{ from: { glob: "assets/*.json" } },
{ from: { glob: "fonts/**" } },
{ from: { glob: "**/*.jpg" } },
{ from: { glob: "**/*.png" } },
], { ignore: [`${relative(appPath, appResourcesFullPath)}/**`] }),
}
All
You need to use the nativescript-worker-loader plugin to create worker threads. Make sure to install the package using the next tag:
npm install nativescript-worker-loader@next --save-dev
Refer to the documentation to migrate your code based on the project type.
Angular
Consider the following code located in app/item/items.component.ts, which uses an absolute path for the templateUrl
:
@Component({
selector: "ns-items",
templateUrl: "app/item/items.component.html"
})
export class ItemsComponent {
...
The legacy workflow used to support the above syntax.
The bundle workflow doesn't support the above syntax. The templateUrl
and styleUrls
paths are resolved relative to the current module.
Convert the absolute paths to relative:
@Component({
selector: "ns-items",
templateUrl: "./items.component.html"
})
export class ItemsComponent {
...
TypeScript
The following code uses the exports
object to export a function:
exports.onTap = function () {
alert("Hi there");
}
The legacy workflow used to support the above syntax.
The bundle workflow doesn't support the above syntax.
Use the export
keyword instead:
export function onTap() {
alert("Hi there");
}
With the bundle workflow, you can now use the ES6 module syntax (import and export) in all types of projects. Prefer it over CommonJS (require and exports) to help webpack eliminate all unused exports from your bundle.
All
Consider the following code located in app/nested-folder/test.ts:
console.log("__dirname", __dirname);
console.log("__filename", __filename);
In the legacy workflow, the project structure used to remain the same in the built application. Executing the code above would have resulted in the following console output:
__dirname /data/data/org.nativescript.ns5/files/app/nested-folder
__filename /data/data/org.nativescript.ns5/files/app/nested-folder/main-page.js
In the bundle workflow, the built application is packaged in a few output files. The file locations are not persisted. The output from the above code will be:
__dirname /data/data/org.nativescript.ns6/files/app
__filename /index.js
You can use module.id
to get the original file path.
console.log(module.id) // ./nested-folder/main-page.ts
However, if you build for production the module.id
will be a number. To enforce webpack to continue using file paths for the module IDs, set the namedModules
optimization property to true
:
module.exports = env => {
…
const config = {
...
optimization: {
namedModules: true
}
}
The __dirname
will point to the absolute path of the bundle file. Using __dirname
and module.id
you can calculate the original absolute file path and directory of the module.
TypeScript
The following code declares an enum without defining it and then uses it:
declare const enum TestEnum {
value1 = 1,
value2 = 2
}
console.log("Printing the second enum value: ", TestEnum.value2);
The above syntax was supported. The legacy workflow used to rely on the nativescript-dev-typescript plugin to perform a TypeScript compilation. The plugin used to start the TypeScript compiler - tsc
.
The bundle workflow uses the ts-loader
in a transpileOnly
mode. The enum values are not inlined. Running the code above results in a reference error:
ReferenceError: TestEnum is not defined
You need to modify a couple of settings in your `webpack.config.js` file. In the configuration of `ts-loader`, set `transpileOnly` to `false`. Also, make sure to remove the `ForkTypeChecking` plugin.