Case-sensitive File Naming for NativeScript TypeScript Source Files

By in
No comments

When working with NativeScript it’s important to pay attention to the casing of filenames for your JavaScript source files, especially when working with TypeScript. When importing classes, interfaces, or other exported TypeScript items from other files, you must make sure that the letter casing in the reference matches the actual source file.

More specifically, the letter casing of your reference needs to match the casing of the actual JavaScript (.js) files that will ultimately be loaded by the NativeScript runtime. After all, TypeScript ultimately gets compiled to JavaScript, and you want to make sure everything lines up!

To demonstrate this, let’s take a quick look at the Hello World TypeScript example on GitHub. The main page screen makes reference to HelloWorldModel, which is an TypeScript class exported from the file main-view-model.ts.

import { EventData } from "data/observable";
import { Page } from "ui/page";
import { HelloWorldModel } from "./main-view-model";
 
// Event handler for Page "navigatingTo" event attached in main-page.xml
export function navigatingTo(args: EventData) {
    // Get the event sender
    var page = <Page>args.object;
    page.bindingContext = new HelloWorldModel();
}

Building the project compiles the TypeScript, revealing the associated .js and .js.map files in the explorer:

view-model-typescript-file

However, if we were to either rename the js file to change its case, OR change the import reference in the page (or both!) so that they don’t match, we’re going to have a problem.

Running this in the android emulator or device results in an immediate crash:

android-emulator-nativescript-filename-crash

with an error message something like:

java.lang.RuntimeException: Unable to start activity ComponentInfo{org.nativescript.pathsample/com.tns.NativeScriptActivity}: com.tns.NativeScriptException: Failed to find module: “./Main-view-model”, relative to: /app/

You might also see something like this in the console:

W/System.err( 1686): at com.tns.Runtime.callJSMethodNative(Native Method)
W/System.err( 1686): at com.tns.Runtime.dispatchCallJSMethodNative(Runtime.java:862)
W/System.err( 1686): at com.tns.Runtime.callJSMethodImpl(Runtime.java:727)
W/System.err( 1686): at com.tns.Runtime.callJSMethod(Runtime.java:713)
W/System.err( 1686): at com.tns.Runtime.callJSMethod(Runtime.java:694)
W/System.err( 1686): at com.tns.Runtime.callJSMethod(Runtime.java:684)

Although the message is certainly specific to our problem, it isn’t clear what that problem is, especially since that file is certainly there (and being a Windows guy, file-casing isn’t something I think about very often!).

Even worse, this error actually does NOT appear when running the iOS version of the app in the simulator. Running this same project in it’s invalid state actually does NOT throw an exception on the Mac!

However, once you do deploy the app on your device, the app will indeed crash, and you’ll see an error like this in the console (assuming it’s attached):

JavaScript stack trace:
@file:///app/main-page.js:2:32
promiseReactionJob@[native code] require@[native code] loadModule@file:///app/tns_modules/globals/globals.js:29:38
resolvePageFromEntry@file:///app/tns_modules/ui/frame/frame-common.js:79:50
navigate@file:///app/tns_modules/ui/frame/frame-common.js:165:40
navigate@file:///app/tns_modules/ui/frame/frame.js:44:43
onLoaded@file:///app/tns_modules/ui/frame/frame.js:38:26
viewDidLoad@file:///app/tns_modules/ui/frame/frame.js:389:27
makeKeyAndVisible@[native code] didFinishLaunchingWithOptions@file:///app/tns_modules/application/application.js:147:39
@[native code] onReceive@file:///app/tns_modules/application/application.js:53:32
UIApplicationMain@[native code] start@file:///app/tns_modules/application/application.js:233:26
anonymous@file:///app/app.js:3:18
evaluate@[native code] moduleEvaluation@[native code] @[native code] promiseReactionJob@[native code] JavaScript error:
file:///app/main-page.js:2:32: JS ERROR Error: Could not find module ‘./Main-view-model’. Computed path ‘/var/containers/Bundle/Application/53C711D8-DE3C-4C0C-8C3C-E9D2FBBB04CD/pathsample.app/app/Main-view-model’.

The error in this case is subtle, but might be more difficult to catch if you’re using camel-casing, where mainViewModel could easily be mistaken for mainviewModel, and since the file is indeed there, it can be a tricky one to resolve.

Worse still, this issue can be compounded by the fact that often users of Visual Studio code will “hide” the generated source (.js) files from the IDE, so you may not even see that there is a problem. Even if you were to rename the TypeScript file, because the original file still exists (and Windows doesn’t have case-sensitive file names) the source files may not be renamed, making it even more difficult to detect the problem.

Similarly, if you are using source control (and you certainly should be!) the .js files are likely excluded from check-in, so when you re-generate them you might be re-introducing the problem over and over as you change environments.

So in case it’s not yet totally clear: pay attention to your file name casing! And if you start seeing funky errors and crashes, especially if it works fine in the emulator but not on the device, double check the file names for both the TypeScript source files and the generated JavaScript files.

As always, I hope this was helpful, and thanks for reading!

The following two tabs change content below.

selaromdotnet

Senior Developer at iD Tech
Josh loves all things Microsoft and Windows, and develops solutions for Web, Desktop and Mobile using the .NET Framework, Azure, UWP and everything else in the Microsoft Stack. His other passion is music, and in his spare time Josh spins and produces electronic music under the name DJ SelArom. His other passion is music, and in his spare time Josh spins and produces electronic music under the name DJ SelArom.