mirror of
https://github.com/mdgriffith/elm-optimize-level-2.git
synced 2024-11-29 12:46:32 +03:00
fixed typescript errors and added a little more explanation for passing unwrapped functions
This commit is contained in:
parent
79ccf4ac7f
commit
d004d9f5dc
79
package-lock.json
generated
79
package-lock.json
generated
@ -1755,6 +1755,12 @@
|
||||
"minimatch": "^3.0.4"
|
||||
}
|
||||
},
|
||||
"immediate": {
|
||||
"version": "3.0.6",
|
||||
"resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
|
||||
"integrity": "sha1-nbHb0Pr43m++D13V5Wu2BigN5ps=",
|
||||
"dev": true
|
||||
},
|
||||
"import-fresh": {
|
||||
"version": "3.2.1",
|
||||
"resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.2.1.tgz",
|
||||
@ -2007,6 +2013,15 @@
|
||||
"pako": "~0.2.5"
|
||||
}
|
||||
},
|
||||
"lie": {
|
||||
"version": "3.3.0",
|
||||
"resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
|
||||
"integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"immediate": "~3.0.5"
|
||||
}
|
||||
},
|
||||
"lines-and-columns": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz",
|
||||
@ -2509,8 +2524,7 @@
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
|
||||
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=",
|
||||
"dev": true,
|
||||
"optional": true
|
||||
"dev": true
|
||||
},
|
||||
"osenv": {
|
||||
"version": "0.1.5",
|
||||
@ -2923,6 +2937,46 @@
|
||||
"integrity": "sha512-9A+PDmgm+2du77B5i0Ip2cxOqqHjgNxnBgglxLcX78A2D6c2rTo61z4jnVABpF4cKeDMDG+cmXXvdnqse2VqMA==",
|
||||
"dev": true
|
||||
},
|
||||
"selenium-webdriver": {
|
||||
"version": "4.0.0-alpha.7",
|
||||
"resolved": "https://registry.npmjs.org/selenium-webdriver/-/selenium-webdriver-4.0.0-alpha.7.tgz",
|
||||
"integrity": "sha512-D4qnTsyTr91jT8f7MfN+OwY0IlU5+5FmlO5xlgRUV6hDEV8JyYx2NerdTEqDDkNq7RZDYc4VoPALk8l578RBHw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"jszip": "^3.2.2",
|
||||
"rimraf": "^2.7.1",
|
||||
"tmp": "0.0.30"
|
||||
},
|
||||
"dependencies": {
|
||||
"jszip": {
|
||||
"version": "3.5.0",
|
||||
"resolved": "https://registry.npmjs.org/jszip/-/jszip-3.5.0.tgz",
|
||||
"integrity": "sha512-WRtu7TPCmYePR1nazfrtuF216cIVon/3GWOvHS9QR5bIwSbnxtdpma6un3jyGGNhHsKCSzn5Ypk+EkDRvTGiFA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"lie": "~3.3.0",
|
||||
"pako": "~1.0.2",
|
||||
"readable-stream": "~2.3.6",
|
||||
"set-immediate-shim": "~1.0.1"
|
||||
}
|
||||
},
|
||||
"pako": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
|
||||
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
|
||||
"dev": true
|
||||
},
|
||||
"rimraf": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
|
||||
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"glob": "^7.1.3"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"semver-compare": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz",
|
||||
@ -2940,6 +2994,12 @@
|
||||
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
|
||||
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
|
||||
},
|
||||
"set-immediate-shim": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz",
|
||||
"integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=",
|
||||
"dev": true
|
||||
},
|
||||
"setimmediate": {
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
|
||||
@ -3159,9 +3219,9 @@
|
||||
}
|
||||
},
|
||||
"terser": {
|
||||
"version": "5.0.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.0.0.tgz",
|
||||
"integrity": "sha512-olH2DwGINoSuEpSGd+BsPuAQaA3OrHnHnFL/rDB2TVNc3srUbz/rq/j2BlF4zDXI+JqAvGr86bIm1R2cJgZ3FA==",
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.1.0.tgz",
|
||||
"integrity": "sha512-pwC1Jbzahz1ZPU87NQ8B3g5pKbhyJSiHih4gLH6WZiPU8mmS1IlGbB0A2Nuvkj/LCNsgIKctg6GkYwWCeTvXZQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"commander": "^2.20.0",
|
||||
@ -3189,6 +3249,15 @@
|
||||
"integrity": "sha1-84sK6B03R9YoAB9B2vxlKs5nHAo=",
|
||||
"dev": true
|
||||
},
|
||||
"tmp": {
|
||||
"version": "0.0.30",
|
||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.30.tgz",
|
||||
"integrity": "sha1-ckGdSovn1s51FI/YsyTlk6cRwu0=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"os-tmpdir": "~1.0.1"
|
||||
}
|
||||
},
|
||||
"to-fast-properties": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
|
||||
|
@ -41,7 +41,7 @@
|
||||
"node-elm-compiler": "^5.0.4",
|
||||
"prepack": "^0.2.54",
|
||||
"selenium-webdriver": "^4.0.0-alpha.7",
|
||||
"terser": "^5.0.0",
|
||||
"terser": "^5.1.0",
|
||||
"ts-node": "^8.10.2",
|
||||
"tslib": "^2.0.0",
|
||||
"typescript": "^3.9.7",
|
||||
|
2
src/external-untyped-modules.d.ts
vendored
2
src/external-untyped-modules.d.ts
vendored
@ -1,5 +1,5 @@
|
||||
declare module 'node-elm-compiler' {
|
||||
export function compileSync(files: string[], options: object): void;
|
||||
export function compileToStringSync(files: string[], options: object): string;
|
||||
}
|
||||
|
||||
declare module 'tree-sitter-elm';
|
||||
|
37
src/index.ts
37
src/index.ts
@ -2,7 +2,7 @@
|
||||
import program from 'commander';
|
||||
import * as path from 'path';
|
||||
import * as Transform from './transform';
|
||||
import { ObjectUpdate, Transforms, InlineLists } from './types';
|
||||
import { Transforms } from './types';
|
||||
import { compileToStringSync } from 'node-elm-compiler';
|
||||
import * as fs from 'fs';
|
||||
const { version } = require('../package.json');
|
||||
@ -38,19 +38,15 @@ program
|
||||
// 'transform into a more modern JS to save size (es2018)',
|
||||
// false
|
||||
// )
|
||||
.option(
|
||||
'--output',
|
||||
'The name of the javascript file to create.',
|
||||
'elm.js'
|
||||
)
|
||||
.option('--output', 'The name of the javascript file to create.', 'elm.js')
|
||||
.parse(process.argv);
|
||||
|
||||
type CLIOptions = {
|
||||
modernize: boolean;
|
||||
excludeTransforms: string[];
|
||||
};
|
||||
// type CLIOptions = {
|
||||
// modernize: boolean;
|
||||
// excludeTransforms: string[];
|
||||
// };
|
||||
|
||||
async function run(filePath: string | undefined, options: CLIOptions) {
|
||||
async function run(filePath: string | undefined) {
|
||||
if (!filePath || !filePath.endsWith('.elm')) {
|
||||
console.error('Please provide a path to an Elm file.');
|
||||
program.outputHelp();
|
||||
@ -78,7 +74,6 @@ async function run(filePath: string | undefined, options: CLIOptions) {
|
||||
// withExcluded.inlineFunctions && withExcluded.passUnwrappedFunctions,
|
||||
// };
|
||||
|
||||
|
||||
const source: string = compileToStringSync([fileName], {
|
||||
output: 'output/elm.opt.js',
|
||||
cwd: dirname,
|
||||
@ -86,8 +81,8 @@ async function run(filePath: string | undefined, options: CLIOptions) {
|
||||
processOpts:
|
||||
// ignore stdout
|
||||
{
|
||||
stdio: ['pipe', 'ignore', 'pipe']
|
||||
}
|
||||
stdio: ['pipe', 'ignore', 'pipe'],
|
||||
},
|
||||
});
|
||||
const transformed = await Transform.transform(
|
||||
dirname,
|
||||
@ -95,15 +90,13 @@ async function run(filePath: string | undefined, options: CLIOptions) {
|
||||
source,
|
||||
false,
|
||||
defaultOptions
|
||||
)
|
||||
);
|
||||
|
||||
fs.writeFileSync(program.output, transformed);
|
||||
console.log("Success!");
|
||||
console.log("")
|
||||
console.log(` ${fileName} ---> ${program.output}`)
|
||||
console.log("")
|
||||
|
||||
|
||||
console.log('Success!');
|
||||
console.log('');
|
||||
console.log(` ${fileName} ---> ${program.output}`);
|
||||
console.log('');
|
||||
}
|
||||
|
||||
run(program.args[0], program.opts() as any).catch(e => console.error(e));
|
||||
run(program.args[0]).catch(e => console.error(e));
|
||||
|
@ -1,5 +1,3 @@
|
||||
|
||||
|
||||
/*
|
||||
This handles functions for operations that this tool doesnt provide as a CLI, but we likely want to study when capturing metrics.
|
||||
So
|
||||
@ -13,10 +11,7 @@ So
|
||||
import * as fs from 'fs';
|
||||
import { prepackFileSync } from 'prepack';
|
||||
import * as Terser from 'terser';
|
||||
import { execSync } from 'child_process';
|
||||
import * as Compress from "@gfx/zopfli";
|
||||
import { resolveModuleName } from 'typescript';
|
||||
|
||||
import * as Compress from '@gfx/zopfli';
|
||||
|
||||
export function prepack(input: string): string {
|
||||
const { code } = prepackFileSync([input], {
|
||||
@ -27,7 +22,6 @@ export function prepack(input: string): string {
|
||||
return code;
|
||||
}
|
||||
|
||||
|
||||
export async function minify(inputFilename: string, outputFilename: string) {
|
||||
const compress = {
|
||||
toplevel: true,
|
||||
@ -82,16 +76,9 @@ export async function gzip(file: string, output: string) {
|
||||
// --force = overwrite the exisign gzip file if it's there
|
||||
// execSync('gzip --keep --force ' + file);
|
||||
const fileContents = fs.readFileSync(file, 'utf8');
|
||||
const promise = Compress.gzipAsync(fileContents, {})
|
||||
.then(
|
||||
(compressed) => {
|
||||
fs.writeFileSync(
|
||||
output,
|
||||
compressed
|
||||
);
|
||||
}
|
||||
);
|
||||
const promise = Compress.gzipAsync(fileContents, {}).then(compressed => {
|
||||
fs.writeFileSync(output, compressed);
|
||||
});
|
||||
|
||||
await promise;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { parseElm, parseDir, primitives } from './parseElm';
|
||||
import ts from 'typescript';
|
||||
import { createCustomTypesTransformer } from './transforms/variantShapes';
|
||||
@ -39,11 +38,7 @@ export const transform = async (
|
||||
): Promise<string> => {
|
||||
// Compile examples in `testcases/*` folder as js
|
||||
// Run whatever transformations we want on them, saving steps as `elm.{transformation}.js`
|
||||
let source = ts.createSourceFile(
|
||||
'elm.js',
|
||||
jsSource,
|
||||
ts.ScriptTarget.ES2018
|
||||
);
|
||||
let source = ts.createSourceFile('elm.js', jsSource, ts.ScriptTarget.ES2018);
|
||||
|
||||
const elmSource = fs.readFileSync(elmfile, 'utf8');
|
||||
let parsedVariants = parseElm({
|
||||
@ -52,12 +47,14 @@ export const transform = async (
|
||||
source: elmSource,
|
||||
}).concat(primitives);
|
||||
|
||||
|
||||
parsedVariants = parsedVariants.concat(parseDir('elm-packages')).concat(parseDir(dir));
|
||||
parsedVariants = parsedVariants
|
||||
.concat(parseDir('elm-packages'))
|
||||
.concat(parseDir(dir));
|
||||
|
||||
// we dont care about types that have no slots on any variants
|
||||
parsedVariants = parsedVariants.filter((variant) => { return variant.totalTypeSlotCount != 0 });
|
||||
|
||||
parsedVariants = parsedVariants.filter(variant => {
|
||||
return variant.totalTypeSlotCount != 0;
|
||||
});
|
||||
|
||||
const normalizeVariantShapes = createCustomTypesTransformer(
|
||||
parsedVariants,
|
||||
@ -72,12 +69,8 @@ export const transform = async (
|
||||
|
||||
let inlineCtx: InlineContext | undefined;
|
||||
const transformations: any[] = removeDisabled([
|
||||
|
||||
[transforms.variantShapes, normalizeVariantShapes],
|
||||
[
|
||||
transforms.inlineFunctions,
|
||||
createFunctionInlineTransformer(verbose),
|
||||
],
|
||||
[transforms.inlineFunctions, createFunctionInlineTransformer(verbose)],
|
||||
[transforms.inlineEquality, inlineEquality()],
|
||||
[transforms.inlineNumberToString, inlineNumberToString()],
|
||||
[
|
||||
@ -121,5 +114,3 @@ function removeDisabled<T>(list: [null | boolean | undefined, T][]): T[] {
|
||||
|
||||
return newList;
|
||||
}
|
||||
|
||||
|
||||
|
@ -60,13 +60,13 @@ export const inlineEquality = (): ts.TransformerFactory<ts.SourceFile> => contex
|
||||
// NOTE: we're cheating here with the source.
|
||||
// I've manually verified that these are number or string comparisons
|
||||
// So they can safely be converted to ===
|
||||
const overrideIdentifiers: string[] = [
|
||||
'leftFringeRank',
|
||||
'end_',
|
||||
'c',
|
||||
'startTagName',
|
||||
'openChar',
|
||||
];
|
||||
// const overrideIdentifiers: string[] = [
|
||||
// 'leftFringeRank',
|
||||
// 'end_',
|
||||
// 'c',
|
||||
// 'startTagName',
|
||||
// 'openChar',
|
||||
// ];
|
||||
|
||||
function inferIsPrimitive(node: any): boolean {
|
||||
let kind = ts.SyntaxKind[node.kind];
|
||||
|
@ -89,16 +89,16 @@ export const createInlineContext = (): InlineContext => ({
|
||||
});
|
||||
|
||||
function reportInlineTransformResult(ctx: InlineContext) {
|
||||
const { splits, partialApplications, inlined } = ctx;
|
||||
const { inlined } = ctx;
|
||||
|
||||
console.log(`inlining function calls
|
||||
inlined ${inlined.fromRawFunc}
|
||||
`);
|
||||
}
|
||||
|
||||
|
||||
|
||||
export const createFunctionInlineTransformer = (logOverview: boolean): ts.TransformerFactory<ts.SourceFile> => context => {
|
||||
export const createFunctionInlineTransformer = (
|
||||
logOverview: boolean
|
||||
): ts.TransformerFactory<ts.SourceFile> => context => {
|
||||
return sourceFile => {
|
||||
const inlineContext: InlineContext = createInlineContext();
|
||||
|
||||
|
@ -65,9 +65,7 @@ We generate two definitions for a function, but in most cases a function is eith
|
||||
|
||||
If a function is always called with the full number of arguments, the minifier can eliminate our wrapped version (`F2(MyFunction_fn)`) and *also* eliminate the `A2` call, which is explicitly smaller than before.
|
||||
|
||||
# Direct call of Lambdas
|
||||
|
||||
Similar to the above, but focused on lambdas.
|
||||
# Passing unwrapped functions and calling them directly
|
||||
|
||||
Let's say we have some elm code that produces the following js.
|
||||
|
||||
@ -84,12 +82,15 @@ we can transform it to
|
||||
var f = function(func, a, b) {
|
||||
return A2(func, a, b)
|
||||
}, f_unwrapped = function(func, a, b) {
|
||||
return func(a, b)
|
||||
return func(a, b) // <-- direct function call!
|
||||
};
|
||||
|
||||
// note that the lambda is unwrapped as well
|
||||
f_unwrapped(function (a,b) {return a + b;}, 1, 2);
|
||||
```
|
||||
|
||||
This transformation works with separately defined functions too.
|
||||
|
||||
|
||||
|
||||
|
||||
@ -264,10 +265,20 @@ updateSingleRecordManually record =
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
It's worth exploring automating this transformation, though of course there's a question of how much this affects asset size on larger projects.
|
||||
|
||||
However, it's hard to explore further without knowing the actual shape of the records being updated.
|
||||
|
||||
**Future work**
|
||||
Explore more approaches. Next on TODO list:
|
||||
```
|
||||
_Utils_update(old, {a: newA})
|
||||
```
|
||||
to
|
||||
```
|
||||
{...old, a: newA}
|
||||
```
|
||||
|
||||
|
||||
# Inline Equality
|
||||
|
Loading…
Reference in New Issue
Block a user