In part #1 I talked about setting up a Vite project from VSCode. That's only half the job because sooner or later you'll need to share what you made, and maybe one day even make it live. Oh and let's not overlook testing.
So in part #2 we'll pick up on bundling, testing, and obfuscation.
The essence of what comes next is encapsulated in the package.json file in your project root - specifically in the scripts section.
// The default scripts info for a new Vite project using TypeScript
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview"
},One point to make in case you didn't already realise - the dev command references vite only - there's no mention of tsc. This is the point of Vite - it watches for changes, transpiles only what changed, and makes a dynamic bundle that it hot-module swapped into your browser. Long story short, your changed code is likely already live in your browser before you've even switched windows to see it. And that's with a decently large code base - mine has 25 separate module files and one of those is over 4k lines long, so its no slouch.
Also, you may be wondering what the difference between the dev, build and preview commands are. So npm run dev starts a local web server with Hot Module Replacement for development, and will automatically change when code changes. Meanwhile npm run build builds the project, and outputs to the folder ./dist. And finally npm run preview starts a local web server that serves the built solution from ./dist for previewing.
Beware that it is necessary to run build before preview and the latter gives you the latest build files - it will not auto-update.
Testing
Ok I'm going to disappoint you early on this one. My driving use-case was for a text-focussed browser component. I started off thinking of using Puppeteer or some other headless browser, but it quickly emerged that whilst the built-in screen grabbing capabilities might have been useful, the sheer volume of pixel-by-pixel checking I needed to confirm operation was not going to work that way.
Instead I pursued a nodejs solution, which required a whole other head-switch to make the component work both in the browser and in node. But I got there and wrote all about over here Konva + TypeScript - DIY test automation with vite-node.
The foundation of that was using Vite-node, the powerhouse in the background of Vite. I couldn't use Vite-test because it didn't get on with the npm node-canvas package which I think was something to do with threading.
I later added in npm packages for pdfkit and fontkit to power font metric lookups in node. Despite all those surface dependencies and the hundreds of under-the-covers sub-depends that they introduce, it all works very well and I generally develop with the tests running in the background every time a source file is saved.
Since I wrote that other post about the detail of my testing approach, I'll skip the detail but I will show you my package.json test command.
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
"test": "nodemon --exec vite-node --script ./src/<your head test code>.ts"
},The new guy here is the test command. This uses nodemon to monitor the app folders for changes - when it sees something change it executes vite-node. The magic here is that we can pass that a TypeScript file and vite-node will transpile it and execute it.
Think about that - it means you can stick with TS, and get immediate change-powered testing at the speed of Vite! And since the TS script runs under nodejs you get to use all of the npm packages like path and fs which means that you can automatically read test instruction files and store test results and analysis. Which is exactly what my test setup does - I have a single test runner which is data-driven by individual JSON test files that define test setup and step scenarios and expected results. Oh, and it can get screen-grabs of the canvas too.
I don't want to overblow it, the point is that vite-node gives you an opportunity for a way in to a TS-based testing world which can be simple, repeatable, consistent and automated. And of course you can borrow that vite-node command for timer-based test launching as part of your nightly build processes.
Obfuscation
Alright, now lets look at how to obscure our code. The why of this is because we might want or need to try to make it difficult for people to copy our work. You hopefully know by now that the JavaScript that makes web apps sing & dance is downloaded to your browser as plain text - everybody can read all of it. There's no away to make that totally secret, but we can make it less readable, and a whole lot obfuscated.
I did a lot of research on this subject - there are many tools around both in the commercial space and available for free. As we discussed, Vite includes Rollup for its bundler and that has a plugin for Terser which can do some minification. But I finally opted for JavaScript-Obfuscator, partly because I like the logo but mostly because of its power.
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
"test": "nodemon --exec vite-node --script ./src/<your head test code>.ts"
"obs": "node obfuscate.cjs"
},This is the obfuscate command in my package.json. You'll need one more package which is:
npm install javascript-obsfuscator --save-devThat package.json obs command asks node to run a JS file, which internally looks like this:
/**
* Obfuscate JS code.
* If you are using TS then you must transpile and package with something like
* webpack or vite-build first. This process works on JS, not TS.
*/
console.log('Obfuscate starts ' + process.env.npm_config_inputPath)
const
fs = require("fs"),
path = require("path"),
jsObs = require("javascript-obfuscator"),
filePath = process.env.npm_config_inputPath,
outputPath = filePath.replace(".js", "-min.js"),
settings = {
compact: true
},
content = fs.readFileSync(filePath, "utf8"),
lic = fs.readFileSync("./legal_statement.txt", "utf8"),
obfucator = jsObs.obfuscate(content, settings);
let obfuscated = lic + "\n" + obfucator.getObfuscatedCode();
fs.writeFileSync(outputPath, obfuscated, {
encoding: "utf8",
flag: "w"
})
console.log('Obfuscate ends ' + outputPath + ' sizes ' + content.length + ' / ' + obfuscated.length )And we run it from the command window via:
npm run obs --inputPath=<path to the file to process>.jsThe gist of the process is to read the given file, use the obfuscator to process it - there are a heap of optional obfuscation settings but I only use the compact option - then write the processed string out to a file of the same same name with the -min.js suffix added. There is a little addition of pulling in a legal statement to include at the head of the new file because my build process strips all comments, so this is effectively adding these back in.
As per the note in the js file, this obfuscation process works on your final JS - the code you would have released to your users. Now you can release the obfuscated file knowing that you've made it more difficult for anyone to steal your hard learned techniques.
One negative of obfuscation is that file sizes increase - as a rule of thumb expect double the original JS file size. But at the same time expect a reduction to between one fifth and one tenth of that size when the file is gzipped by your web server. Its a trade-off and there may be other approaches like chunking modules etc that can both leave you with the protection of obfuscation and keep things speedy.
Wrapping it up
I'm feeling my way through the full build-bundle-test-obfuscate-test-release process so I can't give a final conclusion but I'll update the post when I've got some miles under the wheels.
Summary
We've looked at an approach to scaffolding for testing and obfuscation in my Vite + TS project setup. And we've got cut-out-and-keep information about how to use JavaScript Obfuscator in an npm command. I hope you got the sense of how simple to setup, solid and fast Vite is compared to the other transpilers. If you haven't tried it yet you might be pleased by how much time you get back.
Thanks for reading.
VW. Aug 2024