As NativeScript continues to evolve, some of the features that made sense in 2015 are creating more trouble than they're worth in 2019. One such feature is the so called "short import."
Short imports were intended to improve the developer experience by saving a few key strokes. Instead of this:
import { device } from 'tns-core-modules/platform'
A developer could simply use:
import { device } from 'platform'
It's convenient, but it has created unintended side effects. And with great tooling like VS Code that can automatically add imports, the value of this feature low.
So, now it's deprecated (as of {N} 5.2). Read all about it on the NativeScript blogs.
Now what?
While short imports still work, they will become a real problem with NativeScript 6.0, when webpack becomes the default and only available {N} build system. Webpack expects the full import path, so short imports quickly cause webpack problems.
But if you are maintaining an app that has been around for a while, removing short imports could be quite a chore. In my case, the app I was updating had more than 275 short imports to fix!
I started to fix the imports manually and quickly realized that was a mistake.
If only there was a way to automatically fix all these imports...
VS Code (and RegEx) to the rescue
Everyone knows that VS Code has great search and replace tools, but what may not be as obvious is that you can use regular expressions in the search and replace. Simply toggle "Use Regular Expressions" on and use standard JavaScript regex in the Search, and use $1
, $2
, etc to include matching groups in the Replace.
NOTE: To run really advanced regex searches with backreferences and lookaround (like we'll do today) does require a setting change in VS Code. Go to Settings > Features > Search > Use PCRE2 and check the box to enable these extra capabilities. See the VS Code docs for more context.
And I know, I know. If you have a problem and use RegEx to solve it, now you have two problems. But it beats the tedious alternative.
RegEx to find short imports
The trick to building the right regular expression for this task is trying find short imports for things that should have the tns-core-modules
prefix while avoiding imports for "non-core" modules AND imports that already have the correct long format.
Fortunately, most NativeScript modules follow a similar convention:
- Core modules use "plain" names with no prefix (like
application
orui/layout
) - Non-core modules usually prefix the module name with
nativescript-
orns-
- Local modules referenced by path usually start with
./
or../
or~/
By example, we want to FIND imports that look like this:
import * as imageSource from "image-source";
import * as util from "utils/utils";
import { Image } from "ui/image";
While AVOIDING imports that look like this:
import { Settings } from "../models/app-models";
import * as firebase from "nativescript-plugin-firebase";
import { Color } from "tns-core-modules/color/color";
AND THEN, when we have a match, we want to insert "tns-core-modules/
" in front of the import path while keeping the rest of the line the same.
Put it all together, and we get this search regex:
(from \")(?!nativescript)(?!ns)(?!tns-core-modules)((?:[\w\-\/])+)(\")
Since regex always looks like a cat walked across your keyboard, let's explain in english:
- Start finding imports by searching for
from "
(including the quote) (and store in matching group 1)... - Followed by any number of word characters OR
-
(dashes) OR/
(whacks)(and store in matching group 2)... -
BUT ignore if the matching group contains the words
nativescript
orns
ortns-core-modules
- These are likely non-core modules OR imports that already have the correct long format
And finally, match the trailing
"
(quote)
This combo does a remarkably accurate job of finding the imports we want while ignoring everything else. You can play with a working version of this regex using Regexr in your browser to experiment with what it matches and misses.
When running the search in VS Code, to avoid false positives, you obviously also want to exclude files and folders that are not part of your code, like node_modules
, platforms
, *.js
(assuming you're using TypeScript), .git
.
Replace syntax
When the search finds a match to this pattern, it will create three matching groups with these values:
from "
[name of matched module]
"
For example, when the following line is matched:
import { Image } from "ui/image";
The matching groups will contain:
from "
ui/image
"
We want to insert tns-core-modules/
between group 1 and group 2. Using VS Code, our replace syntax is:
$1tns-core-modules/$2$3
Boom! Job done. Almost...
Another search and replace pass
If you're using the plain JavaScript require()
syntax to import modules, you clearly need a different search pattern. Even in my TypeScript-based app, I had a handful of imports done with require()
for reasons specific to module implementation.
Conceptually, the search regex we need is similar to above:
(require\(\")(globals)+(\"\))
This pattern matches:
require("
globals
")
You could modify the "globals" pattern if you need a wider search. In my case, this was the only require()
in my app that needed the tns-core-modules
prefix.
The replace pattern is the same as before:
$1tns-core-modules/$2$3
Wrap up
Hopefully you've learned two things in this post:
- Eliminating short imports in your NativeScript app doesn't have to be tedious manual process
- VS Code search and replace is REALLY powerful when combined with regex and matching groups
Top comments (0)