Does anyone know how to use the javascript-obfuscator (or similar) in Ember ?
I guess it needs to be called inside ember-cli-build.js but I don't know where and how.
Thank you in advance for any help :)
I don't think there's a really straightforward answer to this. If you're running on embroider, then all your Javascript will be handled by webpack
, so you could try using webpack-obfuscator -- in your ember-cli-build.js
something like
return require('@embroider/compat').compatBuild(app, Webpack, {
plugins: [
new WebpackObfuscator(/*whatever args*/)
],
rules: [
{
test: /\.js$/,
enforce: 'post',
use: {
loader: WebpackObfuscator.loader,
}
}
]
});
The other options I know of would be to write a broccoli plugin. What you're doing is analogous to what ember-cli-terser
does -- post-process Javascript files before they get concatenated together -- so you could use that as reference.
You would need to write a broccoli plugin that actually performs the transformations (the analog is broccoli-terser-sourcemap) and an Ember addon to hook it into ember-cli
's build pipeline (the analog is ember-cli-terser).
Looking at broccoli-terser-sourcemap
's index.js
, which is only 114 lines of code, I would think you could adapt it to something like this:
module.exports = class TerserWriter extends Plugin {
constructor(_inputNodes, options = {}) {
let inputNodes = Array.isArray(_inputNodes) ? _inputNodes : [_inputNodes];
super(inputNodes, {
name: options.name,
annotation: options.annotation,
needsCache: false,
});
this.options = defaults(options, {
obfuscator: {},
});
let exclude = this.options.exclude;
if (Array.isArray(exclude)) {
this.excludes = new MatcherCollection(exclude);
} else {
this.excludes = MatchNothing;
}
}
async build() {
let pendingWork = [];
this.inputPaths.forEach(inputPath => {
walkSync(inputPath).forEach(relativePath => {
if (relativePath.slice(-1) === '/') {
return;
}
let inFile = path.join(inputPath, relativePath);
let outFile = path.join(this.outputPath, relativePath);
fs.mkdirSync(path.dirname(outFile), { recursive: true });
if (this._isJSExt(relativePath) && !this.excludes.match(relativePath)) {
pendingWork.push(() => this.processFile(inFile, outFile, relativePath, this.outputPath));
} else {
symlinkOrCopy.sync(inFile, outFile);
}
});
});
for (let fn of pendingWork) {
await fn();
}
}
_isJSExt(relativePath) {
return relativePath.slice(-3) === '.js' || relativePath.slice(-4) === '.mjs';
}
async processFile(inFile, outFile, relativePath, outDir) {
let input = await readFile(inFile).toString();
let result = obfuscate(input, this.options.obfuscator);
await writeFile(outFile, result.getObfuscatedCode());
}
};
You could also do the worker pooling this that broccoli-terser-sourcemaps
does, and if you care about source maps you'd need to handle them as well, but broccoli-terser-sourcemaps
does just that, so you could use it as reference.
ember-cli
addonember-cli-terser
has even less code -- looking at its index.js
, you could adapt it to something like
'use strict';
module.exports = {
name: require('./package').name,
included(app) {
this._super.included.apply(this, arguments);
let defaultOptions = {
enabled: app.env === 'production',
obfuscator: {
// default `javascript-obfuscator` options
},
};
let addonOptions = app.options['ember-cli-obfuscator'];
this._obfuscatorOptions = Object.assign({}, defaultOptions, addonOptions);
},
postprocessTree(type, tree) {
if (this._obfuscatorOptions.enabled === true && type === 'all') {
// Import the plugin code above
const Obfuscator = require('./broccoli-obfuscator');
return new Obfuscator(tree, this._obfuscatorOptions);
} else {
return tree;
}
}
};
Then you'd have to install the above addon in your app (it could be an in-repo addon), and it should do its thing!
This would definitely take some doing, but what you're doing is so similar to what ember-cli-terser
is doing, just using the obfuscator
API instead of the terser
API, that you have a really good starting point.
BUT, if embroider
is an option for you, I'd definitely try that route first because it might just be a matter of configuration, rather than writing a bunch of code.