diff --git a/cli/lib/asset-plugin.js b/cli/lib/asset-plugin.js new file mode 100644 index 00000000..c15f95ab --- /dev/null +++ b/cli/lib/asset-plugin.js @@ -0,0 +1,75 @@ +/** + * Copyright 2020 Google Inc. All Rights Reserved. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { promises as fs } from 'fs'; +import { basename } from 'path'; + +const defaultOpts = { + prefix: 'asset-url', +}; + +export default function assetPlugin(opts) { + opts = Object.assign({}, defaultOpts, opts); + + /** @type {Map} */ + let assetIdToSourceBuffer; + + const prefix = opts.prefix + ':'; + return { + name: 'asset-plugin', + buildStart() { + assetIdToSourceBuffer = new Map(); + }, + augmentChunkHash(info) { + // Get the sources for all assets imported by this chunk. + const buffers = Object.keys(info.modules) + .map(moduleId => assetIdToSourceBuffer.get(moduleId)) + .filter(Boolean); + + if (buffers.length === 0) return; + + for (const moduleId of Object.keys(info.modules)) { + const buffer = assetIdToSourceBuffer.get(moduleId); + if (buffer) buffers.push(buffer); + } + + const combinedBuffer = + buffers.length === 1 ? buffers[0] : Buffer.concat(buffers); + + return combinedBuffer; + }, + async resolveId(id, importer) { + if (!id.startsWith(prefix)) return; + const realId = id.slice(prefix.length); + const resolveResult = await this.resolve(realId, importer); + + if (!resolveResult) { + throw Error(`Cannot find ${realId}`); + } + // Add an additional .js to the end so it ends up with .js at the end in the _virtual folder. + return prefix + resolveResult.id + '.js'; + }, + async load(id) { + if (!id.startsWith(prefix)) return; + const realId = id.slice(prefix.length, -'.js'.length); + const source = await fs.readFile(realId); + assetIdToSourceBuffer.set(id, source); + this.addWatchFile(realId); + + return `export default import.meta.ROLLUP_FILE_URL_${this.emitFile({ + type: 'asset', + source, + name: basename(realId), + })}`; + }, + }; +} diff --git a/cli/lib/json-plugin.js b/cli/lib/json-plugin.js new file mode 100644 index 00000000..ed0419e2 --- /dev/null +++ b/cli/lib/json-plugin.js @@ -0,0 +1,33 @@ +import {promises as fsp} from "fs"; + +const prefix = "json:"; + +export default function ejsAssetPlugin() { + return { + name: "json-plugin", + async resolveId(id, importer) { + if (!id.startsWith(prefix)) return; + const realId = id.slice(prefix.length); + const resolveResult = await this.resolve(realId, importer); + + if (!resolveResult) { + throw Error(`Cannot find ${realId}`); + } + // Add an additional .js to the end so it ends up with .js at the end in the _virtual folder. + return prefix + resolveResult.id; + }, + async load(id) { + if (!id.startsWith(prefix)) return; + const realId = id.slice(prefix.length); + const source = await fsp.readFile(realId, "utf8"); + + let code = ""; + for(const [key, value] of Object.entries(JSON.parse(source))) { + code += ` + export const ${key} = ${JSON.stringify(value)}; + ` + } + return code; + }, + }; +} diff --git a/cli/package-lock.json b/cli/package-lock.json index 72557c87..29e3693f 100644 --- a/cli/package-lock.json +++ b/cli/package-lock.json @@ -4,23 +4,473 @@ "lockfileVersion": 1, "requires": true, "dependencies": { + "@babel/code-frame": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", + "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", + "dev": true, + "requires": { + "@babel/highlight": "^7.10.4" + } + }, + "@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", + "dev": true + }, + "@babel/highlight": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", + "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "dev": true, + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "chalk": "^2.0.0", + "js-tokens": "^4.0.0" + } + }, + "@rollup/plugin-commonjs": { + "version": "15.0.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-commonjs/-/plugin-commonjs-15.0.0.tgz", + "integrity": "sha512-8uAdikHqVyrT32w1zB9VhW6uGwGjhKgnDNP4pQJsjdnyF4FgCj6/bmv24c7v2CuKhq32CcyCwRzMPEElaKkn0w==", + "dev": true, + "requires": { + "@rollup/pluginutils": "^3.1.0", + "commondir": "^1.0.1", + "estree-walker": "^2.0.1", + "glob": "^7.1.6", + "is-reference": "^1.2.1", + "magic-string": "^0.25.7", + "resolve": "^1.17.0" + } + }, + "@rollup/plugin-node-resolve": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-9.0.0.tgz", + "integrity": "sha512-gPz+utFHLRrd41WMP13Jq5mqqzHL3OXrfj3/MkSyB6UBIcuNt9j60GCbarzMzdf1VHFpOxfQh/ez7wyadLMqkg==", + "dev": true, + "requires": { + "@rollup/pluginutils": "^3.1.0", + "@types/resolve": "1.17.1", + "builtin-modules": "^3.1.0", + "deepmerge": "^4.2.2", + "is-module": "^1.0.0", + "resolve": "^1.17.0" + } + }, + "@rollup/pluginutils": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz", + "integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==", + "dev": true, + "requires": { + "@types/estree": "0.0.39", + "estree-walker": "^1.0.1", + "picomatch": "^2.2.2" + }, + "dependencies": { + "estree-walker": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz", + "integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==", + "dev": true + } + } + }, + "@types/estree": { + "version": "0.0.39", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz", + "integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==", + "dev": true + }, + "@types/node": { + "version": "14.6.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.6.4.tgz", + "integrity": "sha512-Wk7nG1JSaMfMpoMJDKUsWYugliB2Vy55pdjLpmLixeyMi7HizW2I/9QoxsPCkXl3dO+ZOVqPumKaDUv5zJu2uQ==", + "dev": true + }, + "@types/resolve": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz", + "integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==", + "dev": true, + "requires": { + "@types/node": "*" + } + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", + "dev": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "dev": true + }, + "builtin-modules": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.1.0.tgz", + "integrity": "sha512-k0KL0aWZuBt2lrxrcASWDfwOLMnodeQjodT/1SxEQAXsHANgo6ZC/VEaSEHCXt7aSTZ4/4H5LKa+tBXmW7Vtvw==", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, "commander": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/commander/-/commander-6.0.0.tgz", - "integrity": "sha512-s7EA+hDtTYNhuXkTlhqew4txMZVdszBmKWSPEMxGr8ru8JXR7bLUFIAtPhcSuFdJQ0ILMxnJi8GkQL0yvDy/YA==" + "integrity": "sha512-s7EA+hDtTYNhuXkTlhqew4txMZVdszBmKWSPEMxGr8ru8JXR7bLUFIAtPhcSuFdJQ0ILMxnJi8GkQL0yvDy/YA==", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", + "dev": true + }, + "deepmerge": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz", + "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==", + "dev": true + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "estree-walker": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.1.tgz", + "integrity": "sha512-tF0hv+Yi2Ot1cwj9eYHtxC0jB9bmjacjQs6ZBTj82H8JwUywFuc+7E83NWfNMwHXZc11mjfFcVXPe9gEP4B8dg==", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", + "dev": true + }, + "fsevents": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz", + "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==", + "dev": true, + "optional": true + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "dev": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "dev": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "is-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz", + "integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=", + "dev": true + }, + "is-reference": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-1.2.1.tgz", + "integrity": "sha512-U82MsXXiFIrjCK4otLT+o2NA2Cd2g5MLoOVXUZjIOhLurrRxpEXzI8O0KZHr3IjLvlAH1kTPYSuqer5T9ZVBKQ==", + "dev": true, + "requires": { + "@types/estree": "*" + } + }, + "jest-worker": { + "version": "26.3.0", + "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-26.3.0.tgz", + "integrity": "sha512-Vmpn2F6IASefL+DVBhPzI2J9/GJUsqzomdeN+P+dK8/jKxbh8R3BtFnx3FIta7wYlPU62cpJMJQo4kuOowcMnw==", + "dev": true, + "requires": { + "@types/node": "*", + "merge-stream": "^2.0.0", + "supports-color": "^7.0.0" + }, + "dependencies": { + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true + }, + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true }, "json5": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.3.tgz", "integrity": "sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==", + "dev": true, "requires": { "minimist": "^1.2.5" } }, + "magic-string": { + "version": "0.25.7", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz", + "integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==", + "dev": true, + "requires": { + "sourcemap-codec": "^1.4.4" + } + }, + "merge-stream": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", + "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "dev": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, "minimist": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", + "dev": true + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "dev": true, + "requires": { + "wrappy": "1" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==", + "dev": true + }, + "picomatch": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.2.tgz", + "integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==", + "dev": true + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "rollup": { + "version": "2.26.11", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.26.11.tgz", + "integrity": "sha512-xyfxxhsE6hW57xhfL1I+ixH8l2bdoIMaAecdQiWF3N7IgJEMu99JG+daBiSZQjnBpzFxa0/xZm+3pbCdAQehHw==", + "dev": true, + "requires": { + "fsevents": "~2.1.2" + } + }, + "rollup-plugin-terser": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/rollup-plugin-terser/-/rollup-plugin-terser-7.0.2.tgz", + "integrity": "sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==", + "dev": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "jest-worker": "^26.2.1", + "serialize-javascript": "^4.0.0", + "terser": "^5.0.0" + } + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true + }, + "serialize-javascript": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz", + "integrity": "sha512-GaNA54380uFefWghODBWEGisLZFj00nS5ACs6yHa9nLqlLpVLO8ChDGeKRjZnV4Nh4n0Qi7nhYZD/9fCPzEqkw==", + "dev": true, + "requires": { + "randombytes": "^2.1.0" + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "terser": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.3.0.tgz", + "integrity": "sha512-XTT3D3AwxC54KywJijmY2mxZ8nJiEjBHVYzq8l9OaYuRFWeQNBwvipuzzYEP4e+/AVcd1hqG/CqgsdIRyT45Fg==", + "dev": true, + "requires": { + "commander": "^2.20.0", + "source-map": "~0.6.1", + "source-map-support": "~0.5.12" + }, + "dependencies": { + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", + "dev": true } } } diff --git a/cli/package.json b/cli/package.json index b2ab8641..a7fbc953 100644 --- a/cli/package.json +++ b/cli/package.json @@ -2,15 +2,22 @@ "name": "cli", "version": "0.0.1", "description": "", - "main": "index.js", + "bin": { + "squoosh-cli": "build/index.js" + }, "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "build": "rollup -c" }, "keywords": [], "author": "Surma ", "license": "Apache-2.0", - "dependencies": { + "dependencies": {}, + "devDependencies": { + "@rollup/plugin-commonjs": "^15.0.0", + "@rollup/plugin-node-resolve": "^9.0.0", "commander": "^6.0.0", - "json5": "^2.1.3" + "json5": "^2.1.3", + "rollup": "^2.26.11", + "rollup-plugin-terser": "^7.0.2" } } diff --git a/cli/rollup.config.js b/cli/rollup.config.js new file mode 100644 index 00000000..5f290b52 --- /dev/null +++ b/cli/rollup.config.js @@ -0,0 +1,32 @@ +import resolve from "@rollup/plugin-node-resolve"; +import cjs from "@rollup/plugin-commonjs"; +import { terser } from "rollup-plugin-terser"; +import asset from "./lib/asset-plugin.js"; +import json from "./lib/json-plugin.js"; + +export default { + input: "src/index.js", + output: { + dir: "build", + format: "cjs", + assetFileNames: "[name]-[hash][extname]" + }, + plugins: [ + resolve(), + cjs(), + asset(), + json(), + terser({ + mangle: true + }) + ], + external: [ + "os", + "path", + "fs", + "worker_threads", + "events", + "child_process", + "crypto" + ] +}; diff --git a/cli/codecs.js b/cli/src/codecs.js similarity index 52% rename from cli/codecs.js rename to cli/src/codecs.js index 3232e1b1..084b246e 100644 --- a/cli/codecs.js +++ b/cli/src/codecs.js @@ -1,11 +1,41 @@ +import {promises as fsp} from "fs"; -module.exports = { +// MozJPEG +import mozEnc from "../../codecs/mozjpeg/enc/mozjpeg_enc.js"; +import mozEncWasm from "asset-url:../../codecs/mozjpeg/enc/mozjpeg_enc.wasm"; +import mozDec from "../../codecs/mozjpeg/dec/mozjpeg_dec.js"; +import mozDecWasm from "asset-url:../../codecs/mozjpeg/dec/mozjpeg_dec.wasm"; + +// WebP +import webpEnc from "../../codecs/webp/enc/webp_enc.js"; +import webpEncWasm from "asset-url:../../codecs/webp/enc/webp_enc.wasm"; +import webpDec from "../../codecs/webp/dec/webp_dec.js"; +import webpDecWasm from "asset-url:../../codecs/webp/dec/webp_dec.wasm"; + +// AVIF +import avifEnc from "../../codecs/avif/enc/avif_enc.js"; +import avifEncWasm from "asset-url:../../codecs/avif/enc/avif_enc.wasm"; +import avifDec from "../../codecs/avif/dec/avif_dec.js"; +import avifDecWasm from "asset-url:../../codecs/avif/dec/avif_dec.wasm"; + +function instantiateEmscriptenWasm(factory, path) { + if(path.startsWith("file://")) { + path = path.slice("file://".length); + } + return factory({ + locateFile() { + return path; + } + }) +} + +export default { mozjpeg: { name: "MozJPEG", extension: "jpg", detectors: [/^\xFF\xD8\xFF/], - dec: require("../codecs/mozjpeg/dec/mozjpeg_dec.js"), - enc: require("../codecs/mozjpeg/enc/mozjpeg_enc.js"), + dec: () => instantiateEmscriptenWasm( mozDec, mozDecWasm), + enc: () => instantiateEmscriptenWasm( mozEnc, mozEncWasm), defaultEncoderOptions: { quality: 75, baseline: false, @@ -29,8 +59,8 @@ module.exports = { name: "WebP", extension: "webp", detectors: [/^RIFF....WEBPVP8[LX ]/], - dec: require("../codecs/webp/dec/webp_dec.js"), - enc: require("../codecs/webp/enc/webp_enc.js"), + dec: () => instantiateEmscriptenWasm( webpDec, webpDecWasm), + enc: () => instantiateEmscriptenWasm( webpEnc, webpEncWasm), defaultEncoderOptions: { quality: 75, target_size: 0, @@ -65,8 +95,8 @@ module.exports = { name: "AVIF", extension: "avif", detectors: [/^\x00\x00\x00 ftypavif\x00\x00\x00\x00/], - dec: require("../codecs/avif/dec/avif_dec.js"), - enc: require("../codecs/avif/enc/avif_enc.js"), + dec: () => instantiateEmscriptenWasm( avifDec, avifDecWasm), + enc: () => instantiateEmscriptenWasm( avifEnc, avifEncWasm), defaultEncoderOptions: { minQuantizer: 16, maxQuantizer: 16, diff --git a/cli/image_data.js b/cli/src/image_data.js similarity index 76% rename from cli/image_data.js rename to cli/src/image_data.js index 7aea33bc..ee4f96b5 100644 --- a/cli/image_data.js +++ b/cli/src/image_data.js @@ -1,4 +1,4 @@ -module.exports = class ImageData { +export default class ImageData { constructor(data, width, height) { this.data = data; this.width = width; diff --git a/cli/index.js b/cli/src/index.js similarity index 89% rename from cli/index.js rename to cli/src/index.js index fea3cf1c..aab7106e 100644 --- a/cli/index.js +++ b/cli/src/index.js @@ -1,20 +1,20 @@ -const { program } = require("commander"); -const JSON5 = require("json5"); -const { - threadId, +import {program} from "commander"; +import JSON5 from "json5"; +import { Worker, isMainThread, parentPort -} = require("worker_threads"); -const { cpus } = require("os"); -const path = require("path"); -const fsp = require("fs").promises; +} from "worker_threads"; +import { cpus } from "os"; +import {extname,join,basename} from "path"; +import {promises as fsp} from "fs"; +import {version} from "json:../package.json"; -const visdifModule = require("../codecs/visdif/visdif.js"); -const supportedFormats = require("./codecs.js"); +import supportedFormats from "./codecs.js"; // Our decoders currently rely on a `ImageData` global. -globalThis.ImageData = require("./image_data.js"); +import ImageData from "./image_data.js"; +globalThis.ImageData = ImageData; async function decodeFile(file) { const buffer = await fsp.readFile(file); @@ -61,6 +61,7 @@ function jobPromise(worker, msg) { const butteraugliGoal = 1.4; const maxRounds = 8; async function optimize(bitmapIn, encode, decode) { +const visdifModule = require("../codecs/visdif/visdif.js"); let quality = 50; let inc = 25; let butteraugliDistance = 2; @@ -117,8 +118,8 @@ async function processFiles(files) { let i = 0; const jobs = []; for (const file of files) { - const ext = path.extname(file); - const base = path.basename(file, ext); + const ext = extname(file); + const base = basename(file, ext); const bitmap = await decodeFile(file); for (const [encName, value] of Object.entries(supportedFormats)) { @@ -130,7 +131,7 @@ async function processFiles(files) { value.defaultEncoderOptions, JSON5.parse(program[encName]) ); - const outputFile = path.join( + const outputFile = join( program.outputDir, `${base}.${value.extension}` ); @@ -152,7 +153,7 @@ async function processFiles(files) { if (isMainThread) { program - .version(require("./package.json").version) + .version(version) .arguments("") .option("-d, --output-dir ", "Output directory", ".") .action(processFiles);