Skip to content

[WIP][3.x] Make minify portable by making it work without file system#1366

Closed
avdg wants to merge 1 commit intomishoo:masterfrom
avdg:portable-minify
Closed

[WIP][3.x] Make minify portable by making it work without file system#1366
avdg wants to merge 1 commit intomishoo:masterfrom
avdg:portable-minify

Conversation

@avdg
Copy link
Copy Markdown
Contributor

@avdg avdg commented Nov 5, 2016

Current status: Preparing for merge. Open for feedback.

TODO:

  • Give functions custom errors under same new error type
  • Fix regression
  • Look into extra tests for name caching
  • Make readNameCache and writeNameCache internal api, relying on readFile and writeFile as platform depending "drivers"
  • Documentate writeFile and readFile as overwritable functions in portable version. (Not so for simple_glob, which will be unofficial api even though it is as easy to overwrite this function)
  • Make the api work without simple_glob being available.
  • Add tests for portable UglifyJS version
  • Test errors
  • Test readFile individually (should be covered enough?)

EXTRA:

  • Hmmm.. what about async...

@avdg
Copy link
Copy Markdown
Contributor Author

avdg commented Nov 6, 2016

Demo:

  1. Open https://avdg.github.io/uglify/#ref_PR_x2d1366
  2. Open console
  3. Type code, UglifyJS is the uglify object.

Example code:

  • Basic example: UglifyJS.minify("var foo = bar", {fromString: "true"})
  • Source maps: UglifyJS.minify("var foo = function(a, b) { return a + b; }", {fromString: "true", sourceMapUrl: false, outSourceMap: "out.js.map"})

lib/minify.js Outdated
@@ -0,0 +1,125 @@
exports.readFile = exports.simple_glob = exports.readNameCache =
exports.writeNameCache = function() {
throw Error("UglifyJS has no access to the file system");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would prefer a top level named error function followed by four lines with one assignment per line.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done?

lib/minify.js Outdated
if (options.spidermonkey) {
toplevel = AST_Node.from_mozilla_ast(files);
} else {
function addFile(file, fileUrl) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can't declare a function in an if or else:

$ npm test

uglify-js@2.7.4 test /home/travis/build/mishoo/UglifyJS2
node test/run-tests.js
undefined:9153
function addFile(file, fileUrl) {
^^^^^^^^
SyntaxError: In strict mode code, functions can only be declared at top level or immediately within another function.

Copy link
Copy Markdown
Contributor Author

@avdg avdg Nov 6, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, this is the same code (only moved a bit). And didn't fix (although I could) because I spend time doing other things apparently.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor

@kzc kzc Nov 6, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

related: #1052, #1053

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be fixed

@kzc
Copy link
Copy Markdown
Contributor

kzc commented Nov 6, 2016

Assuming all tests pass, the patch looks fine.

Although not much code was changed in minify(), the mangle name caches may not have been tested extensively in the previously existing minify() tests.

Is it possible for users to override the four minify functions that use the file system to provide their own browser equivalents of a fake file system if they wanted?

@avdg
Copy link
Copy Markdown
Contributor Author

avdg commented Nov 6, 2016

Is it possible for users to override the four minify functions that use the file system to provide their own browser equivalents of a fake file system if they wanted?

Yes, export is the same object as UglifyJS. As long as users can reach that code, users can change the functionality.

@avdg
Copy link
Copy Markdown
Contributor Author

avdg commented Nov 6, 2016

I might remove readNameCache and writeNameCache as drivers (like we done it) and make it internal api. readNameCache depends on reading a file, which can be done by the readFile "driver" (note the quotes) and I'll add a writeFile "driver" fro writeNameCache.

@avdg
Copy link
Copy Markdown
Contributor Author

avdg commented Nov 6, 2016

Hmm I have to see, maybe we should allow external api to pass data to these read/write nameCache properties in some way (without changing other api's). I have to see how to refactor this yet.

@avdg
Copy link
Copy Markdown
Contributor Author

avdg commented Nov 6, 2016

I won't make simple_glob platform independent for now though... Maybe I can change the code so 'globbing' would only be done when the function is available.

@kzc
Copy link
Copy Markdown
Contributor

kzc commented Nov 7, 2016

There's no files to glob in the browser (no equivalent of fs.readdirSync), so a no op is fine.

@kzc
Copy link
Copy Markdown
Contributor

kzc commented Nov 7, 2016

To abstract the file system, hooks would be needed for these cases:

$ egrep '(fs|path)\.' tools/node.js 
    return fs.realpathSync(path.join(path.dirname(__filename), file));
    return fs.readFileSync(file, "utf8");
                : fs.readFileSync(file, "utf8");
        inMap = JSON.parse(fs.readFileSync(options.inSourceMap, "utf8"));
    var data = fs.readFileSync(filename, "utf8");
    return readReservedFile(path.join(__dirname, "domprops.json"), reserved);
            var cache = fs.readFileSync(filename, "utf8");
            data = fs.readFileSync(filename, "utf8");
        fs.writeFileSync(filename, JSON.stringify(data, null, 2), "utf8");
        var dir = path.dirname(glob);
            var entries = fs.readdirSync(dir);
            var pattern = "^" + (path.basename(glob)

They needn't be implemented for the browser.

@avdg
Copy link
Copy Markdown
Contributor Author

avdg commented Nov 7, 2016

readNameCache and writeNameCache won't be part of the official api unless given some thought (maybe an api for name cache without fs access is doable), although available for convenience for the still unadopted bin/uglify that uses them.

@avdg
Copy link
Copy Markdown
Contributor Author

avdg commented Dec 19, 2016

Going to move the current progress to harmony...avdg:portable-minify-backup

Then rebase this patch to be mergeable with master again.

lib/minify.js Outdated
@@ -0,0 +1,168 @@
exports.readFile = function() {
DefaultsError.croak("UglifyJS has no access to read from the file system");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Error message is too verbose - "readFile not supported" is sufficient. Ditto for writeFile and simple_glob.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@kzc
Copy link
Copy Markdown
Contributor

kzc commented Dec 20, 2016

You could add a test in test/mocha/cli.js for the --self version of minify() with fromString: true.

@avdg
Copy link
Copy Markdown
Contributor Author

avdg commented Dec 20, 2016

Ow yeah, these tests there generate the portable version.

@avdg
Copy link
Copy Markdown
Contributor Author

avdg commented Dec 24, 2016

Added few tests on the hooks (and fromString). There are still some additional tests needed for readNameCache, writeNameCache and the errors (as it seems that the errors simply don't show up).

};

exports.simple_glob = function(files) {
return files;
Copy link
Copy Markdown
Contributor

@kzc kzc Dec 25, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should return an array of strings if a scalar is passed:

exports.simple_glob = function(files) {
    return Array.isArray(files) ? files : [ files ];
}

as per comments in the original implementation:

// Argument `glob` may be a string or an array of strings.
// Returns an array of strings. Garbage in, garbage out.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems the current code handles string as parameter fine, adding test as extra insurance for now.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

concat handles this flexible, non-arrays are implicitly treated as an array with 1 element.

https://github.com/mishoo/UglifyJS2/pull/1366/files/0750f4a338cfdc557d96fc68382751c0b23d873a#diff-29c0119b6a96bae34223c40f1c7f774bL79

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

okay.

});

it("Should minify from a string successfully", function() {
assert.strictEqual(minifier.minify('console.log("Hello world!");', {fromString: true}).code,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggest minifying 'console["log"]("Hello " + "world!");' in all your examples in this file to show that minify() actually did something.

@avdg
Copy link
Copy Markdown
Contributor Author

avdg commented Dec 25, 2016

I think I'm renaming simple_glob to glob just because the glob doesn't have to be simple.

@kzc
Copy link
Copy Markdown
Contributor

kzc commented Dec 25, 2016

I think I'm renaming simple_glob to glob just because the glob doesn't have to be simple.

It's already part of the external API and shouldn't be renamed.

@avdg
Copy link
Copy Markdown
Contributor Author

avdg commented Dec 25, 2016

I think I'm renaming simple_glob to glob just because the glob doesn't have to be simple.

It's already part of the external API and shouldn't be renamed.

That's unfortunate then...

@avdg
Copy link
Copy Markdown
Contributor Author

avdg commented Dec 26, 2016

About the renaming, maybe it might be listed in a separate issue, ready to fill any gap of opportunity. Given that minifiers are getting competition and that this is a relative small task that leads to better consistency of the api this might be an alternative option.

This will also give more reasons to keep simple_glob part of the unofficial api, which is already an upgrade from being undocumented.

@kzc
Copy link
Copy Markdown
Contributor

kzc commented Dec 26, 2016

Please keep the external API the same. You can add to it, but not remove stuff from it.

Remember that uses_arguments was also undocumented but used in the wild: #1299

@kzc
Copy link
Copy Markdown
Contributor

kzc commented Dec 26, 2016

Given that minifiers are getting competition and that this is a relative small task that leads to better consistency of the api this might be an alternative option.

If there's an UglifyJS2 3.x release then the external API can be altered. Probably the same time as minify() is dropped into bin/uglifyjs and/or harmony is merged into master - after the ES6 scope and compress bugs are fixed.

Another thing that should be done in a hypothetical 3.x release is to flatten the overly complicated uglify options that currently has 4 or 5 different sub-sections: mangle, compress, mangleProps, and output (a.k.a. beautify) - and unify the options between bin/uglify and minify().

Copy link
Copy Markdown
Contributor Author

@avdg avdg left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't set options.mangleProperties.cache if options.mangleProperties is false (must convert to an object like value first)


// 3. mangle properties
if (options.mangleProperties || options.nameCache) {
options.mangleProperties.cache = readNameCache(options.nameCache, "props");
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

options.mangleProperties defaults to false, which isn't an object :/ Need to know how this stuff is working before I can fix this...

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code seems to come from #910 and isn't actually tested properly

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just using default value to fix this...

@avdg
Copy link
Copy Markdown
Contributor Author

avdg commented Apr 10, 2017

I really hope I didn't do anything wrong and that the test coverage is good. It's easy to lose track of changes because lib/minify.js file is maintained without an easy way to keep it in sync.

@avdg
Copy link
Copy Markdown
Contributor Author

avdg commented Apr 10, 2017

Gonna attempt to stage it as is, additional features can be added and there is nothing structural anyway.

@avdg avdg changed the title [WIP] Make minify portable by making it work without file system Make minify portable by making it work without file system Apr 10, 2017
@avdg
Copy link
Copy Markdown
Contributor Author

avdg commented Apr 10, 2017

Did a manual diff of the minify function, should look good

@alexlamsl
Copy link
Copy Markdown
Collaborator

Much better - only Travis CI is upset now:

https://travis-ci.org/mishoo/UglifyJS2/jobs/220551459#L244-L247

I think we need to pull addFile() out of the else block:

https://github.com/mishoo/UglifyJS2/pull/1366/files#diff-465a5a39285d7d0ab958a973c3d58432R96

@avdg
Copy link
Copy Markdown
Contributor Author

avdg commented Apr 10, 2017

oh yeah, it should be done differently, easy peasy...

@avdg
Copy link
Copy Markdown
Contributor Author

avdg commented Apr 10, 2017

well, moving outside the else block shouldn't be enough... I defined it as an inline function.

@avdg avdg force-pushed the portable-minify branch from 64b2900 to 3b01516 Compare April 10, 2017 14:14
exports["SymbolDef"] = SymbolDef;
exports["minify"] = minify;
exports["readNameCache"] = readNameCache;
exports["writeNameCache"] = writeNameCache;
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not export readNameCache & writeNameCache, as we never do before anyway.

Copy link
Copy Markdown
Contributor Author

@avdg avdg Apr 10, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

bin/uglifyjs depends on it

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we might do a deprecation policy

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah yes, it is exported directly under tools/node.js prior to this PR.

@avdg avdg force-pushed the portable-minify branch from 3b01516 to 96f0793 Compare April 10, 2017 14:29
@avdg
Copy link
Copy Markdown
Contributor Author

avdg commented Apr 10, 2017

Just removed a whitespace

@alexlamsl
Copy link
Copy Markdown
Collaborator

Please update #1366 (diff) to use UglifyJS.base64Decoder

@avdg avdg force-pushed the portable-minify branch from 96f0793 to 9b16a75 Compare April 10, 2017 16:30
@avdg
Copy link
Copy Markdown
Contributor Author

avdg commented Apr 10, 2017

Done so for UglifyJS.base64Decoder, implemented UglifyJS.base64Encoder as well.

};

var defaultBase64Decoder = exports.base64Decoder = function(input) {
DefaultsError.croak("No base64 decoder implemented");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should fallback to using atob here if typeof atob == "function".

}

var defaultBase64Encoder = exports.base64Encoder = function(input) {
DefaultsError.croak("No base64 encoder implemented");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should fallback to using btoa here if typeof btoa == "function".

@avdg
Copy link
Copy Markdown
Contributor Author

avdg commented Apr 10, 2017

  • Add default fallback for web environment
  • Add tests

@alexlamsl alexlamsl changed the title Make minify portable by making it work without file system [WIP][3.x] Make minify portable by making it work without file system Apr 11, 2017
alexlamsl added a commit to alexlamsl/UglifyJS that referenced this pull request Apr 13, 2017
- refactor `screw_ie8` to `ie8`
- compact `sourceMap` options
- more stringent verification on input `options`
- toplevel shorthands
  - `ie8`
  - `keep_fnames`
  - `toplevel`

closes mishoo#96
closes mishoo#1366
fixes mishoo#124
fixes mishoo#263
fixes mishoo#379
fixes mishoo#423
fixes mishoo#576
fixes mishoo#737
fixes mishoo#958
fixes mishoo#1036
fixes mishoo#1175
fixes mishoo#1220
fixes mishoo#1223
fixes mishoo#1280
alexlamsl added a commit to alexlamsl/UglifyJS that referenced this pull request Apr 13, 2017
- refactor `screw_ie8` to `ie8`
- compact `sourceMap` options
- more stringent verification on input `options`
- toplevel shorthands
  - `ie8`
  - `keep_fnames`
  - `toplevel`
- deprecated `fromString` in `minify()`
  - `minify()` no longer handles any `fs` operations
- unify order of operations for `mangle_properties()` on CLI & API
  - `bin/uglifyjs` used to `mangle_properties()` before even `Compressor`
  - `minify()` used to `mangle_properties()` after `Compressor` but before `mangle_names()`
  - both will now do `Compressor`, `mangle_names()` then `mangle_properties()`

closes mishoo#96
closes mishoo#1366
fixes mishoo#124
fixes mishoo#263
fixes mishoo#379
fixes mishoo#423
fixes mishoo#576
fixes mishoo#737
fixes mishoo#958
fixes mishoo#1036
fixes mishoo#1175
fixes mishoo#1220
fixes mishoo#1223
fixes mishoo#1280
alexlamsl added a commit to alexlamsl/UglifyJS that referenced this pull request Apr 13, 2017
- refactor `screw_ie8` to `ie8`
- compact `sourceMap` options
- more stringent verification on input `options`
- toplevel shorthands
  - `ie8`
  - `keep_fnames`
  - `toplevel`
- deprecated `fromString` in `minify()`
  - `minify()` no longer handles any `fs` operations
- unify order of operations for `mangle_properties()` on CLI & API
  - `bin/uglifyjs` used to `mangle_properties()` before even `Compressor`
  - `minify()` used to `mangle_properties()` after `Compressor` but before `mangle_names()`
  - both will now do `Compressor`, `mangle_names()` then `mangle_properties()`

closes mishoo#96
closes mishoo#1366
fixes mishoo#124
fixes mishoo#263
fixes mishoo#379
fixes mishoo#423
fixes mishoo#576
fixes mishoo#737
fixes mishoo#958
fixes mishoo#1036
fixes mishoo#1175
fixes mishoo#1220
fixes mishoo#1223
fixes mishoo#1280
alexlamsl added a commit to alexlamsl/UglifyJS that referenced this pull request Apr 14, 2017
- refactor `screw_ie8` to `ie8`
- compact `sourceMap` options
- more stringent verification on input `options`
- toplevel shorthands
  - `ie8`
  - `keep_fnames`
  - `toplevel`
- deprecated `fromString` in `minify()`
  - `minify()` no longer handles any `fs` operations
- unify order of operations for `mangle_properties()` on CLI & API
  - `bin/uglifyjs` used to `mangle_properties()` before even `Compressor`
  - `minify()` used to `mangle_properties()` after `Compressor` but before `mangle_names()`
  - both will now do `Compressor`, `mangle_names()` then `mangle_properties()`
- `options.parse` / `--parse` for parser options beyond `bare_returns`

closes mishoo#96
closes mishoo#1366
fixes mishoo#124
fixes mishoo#263
fixes mishoo#379
fixes mishoo#423
fixes mishoo#576
fixes mishoo#737
fixes mishoo#958
fixes mishoo#1036
fixes mishoo#1175
fixes mishoo#1220
fixes mishoo#1223
fixes mishoo#1280
alexlamsl added a commit to alexlamsl/UglifyJS that referenced this pull request Apr 14, 2017
- refactor `screw_ie8` to `ie8`
- compact `sourceMap` options
- more stringent verification on input `options`
- toplevel shorthands
  - `ie8`
  - `keep_fnames`
  - `toplevel`
- deprecated `fromString` in `minify()`
  - `minify()` no longer handles any `fs` operations
- unify order of operations for `mangle_properties()` on CLI & API
  - `bin/uglifyjs` used to `mangle_properties()` before even `Compressor`
  - `minify()` used to `mangle_properties()` after `Compressor` but before `mangle_names()`
  - both will now do `Compressor`, `mangle_names()` then `mangle_properties()`
- `options.parse` / `--parse` for parser options beyond `bare_returns`

closes mishoo#96
closes mishoo#1366
fixes mishoo#124
fixes mishoo#263
fixes mishoo#379
fixes mishoo#423
fixes mishoo#576
fixes mishoo#737
fixes mishoo#958
fixes mishoo#1036
fixes mishoo#1175
fixes mishoo#1220
fixes mishoo#1223
fixes mishoo#1280
@avdg
Copy link
Copy Markdown
Contributor Author

avdg commented Apr 14, 2017

I'm quite busy for now as I do lots of interviews, so here my apology that I even can't do simple fixes that requires more than 1 click.

@alexlamsl
Copy link
Copy Markdown
Collaborator

@avdg no worries - I think #1811 will eventually take over 👍

alexlamsl added a commit that referenced this pull request Apr 15, 2017
- rename `screw_ie8` to `ie8`
- rename `mangle.except` to `mangle.reserved`
- rename `mangle.properties.ignore_quoted` to `mangle.properties.keep_quoted` 
- compact `sourceMap` options
- more stringent verification on input `options`
- toplevel shorthands
  - `ie8`
  - `keep_fnames`
  - `toplevel`
  - `warnings`
- support arrays and unquoted string values on CLI
- drop `fromString` from `minify()`
  - `minify()` no longer handles any `fs` operations
- unify order of operations for `mangle_properties()` on CLI & API
  - `bin/uglifyjs` used to `mangle_properties()` before even `Compressor`
  - `minify()` used to `mangle_properties()` after `Compressor` but before `mangle_names()`
  - both will now do `Compressor`, `mangle_names()` then `mangle_properties()`
- `options.parse` / `--parse` for parser options beyond `bare_returns`
- add `mangle.properties.builtins` to disable built-in reserved list
  - disable with `--mangle-props builtins` on CLI
- `warnings` now off by default
- add `--warn` and `--verbose` on CLI
- drop `--enclose`
- drop `--export-all`
- drop `--reserved-file`
  - use `--mangle reserved` instead
- drop `--reserve-domprops`
  - enabled by default, disable with `--mangle-props domprops`
- drop `--prefix`
  - use `--source-map base` instead
- drop `--lint`
- remove `bin/extract-props.js`
- limit exposure of internal APIs
- update documentations

closes #96
closes #102
closes #136
closes #166
closes #243
closes #254
closes #261
closes #311
closes #700
closes #748
closes #912
closes #1072
closes #1366
fixes #101
fixes #123
fixes #124
fixes #263
fixes #379
fixes #419
fixes #423
fixes #461
fixes #465
fixes #576
fixes #737
fixes #772
fixes #958
fixes #1036
fixes #1142
fixes #1175
fixes #1220
fixes #1223
fixes #1280
fixes #1359
fixes #1368
@avdg
Copy link
Copy Markdown
Contributor Author

avdg commented Apr 15, 2017

Hmm.. I might want to port the tests to the latest master

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants