Repository: lasso-js/lasso Branch: master Commit: 30fe251247ef Files: 1072 Total size: 857.7 KB Directory structure: gitextract_n7scrtee/ ├── .editorconfig ├── .eslintrc ├── .gitignore ├── .npmignore ├── .travis.yml ├── CHANGELOG.md ├── README.md ├── browser-refresh/ │ └── index.js ├── docs/ │ ├── amd.md │ ├── bundling.md │ ├── javascript-api.md │ └── migration-optimizer-to-lasso.md ├── getImageInfo.js ├── index.js ├── middleware/ │ ├── index.js │ ├── koa/ │ │ └── serveStatic.js │ └── serveStatic.js ├── node-require-no-op.js ├── package.json ├── src/ │ ├── AsyncPackage.js │ ├── Bundle.js │ ├── BundleConfig.js │ ├── BundleMappings.js │ ├── BundleSetConfig.js │ ├── Config.js │ ├── DependencyList.js │ ├── DependencyTree.js │ ├── FlagSet.js │ ├── InlinePos.js │ ├── Lasso.js │ ├── LassoCache.js │ ├── LassoContext.js │ ├── LassoManifest.js │ ├── LassoPageResult.js │ ├── LassoPrebuildResult.js │ ├── LoaderMetadata.js │ ├── PageBundles.js │ ├── PageConfig.js │ ├── Slot.js │ ├── SlotTracker.js │ ├── browser-refresh/ │ │ └── index.js │ ├── bundle-builder.js │ ├── bundling-strategies.js │ ├── caching-fs.js │ ├── condition.js │ ├── config-loader.js │ ├── content-types.js │ ├── dependencies/ │ │ ├── Dependency.js │ │ ├── DependencyRegistry.js │ │ ├── RequireHandler.js │ │ ├── dependency-comment.js │ │ ├── dependency-dependencies.js │ │ ├── dependency-intersection.js │ │ ├── dependency-package.js │ │ ├── dependency-resource.js │ │ ├── glob.js │ │ └── index.js │ ├── dependency-walker.js │ ├── flags.js │ ├── index.js │ ├── last-modified.js │ ├── manifest-loader.js │ ├── middleware/ │ │ ├── index.js │ │ ├── koa/ │ │ │ └── serveStatic.js │ │ └── serveStatic.js │ ├── node-require-no-op/ │ │ └── index.js │ ├── page-bundles-builder.js │ ├── path.js │ ├── plugins/ │ │ ├── lasso-image/ │ │ │ ├── index.js │ │ │ ├── lasso-image-browser.js │ │ │ └── package.json │ │ ├── lasso-minify-css/ │ │ │ └── index.js │ │ ├── lasso-minify-js/ │ │ │ └── index.js │ │ └── lasso-resolve-css-urls/ │ │ └── index.js │ ├── reader.js │ ├── require/ │ │ ├── build-plugin-config.js │ │ ├── dep-require-remap.js │ │ ├── dep-require.js │ │ ├── dep-runtime.js │ │ ├── dep-transport-builtin.js │ │ ├── dep-transport-define.js │ │ ├── dep-transport-installed.js │ │ ├── dep-transport-loader-metadata.js │ │ ├── dep-transport-main.js │ │ ├── dep-transport-ready.js │ │ ├── dep-transport-remap.js │ │ ├── dep-transport-run.js │ │ ├── dep-transport-search-path.js │ │ ├── index.js │ │ ├── inspect-cache.js │ │ └── util/ │ │ ├── Deduper.js │ │ ├── DeduperContext.js │ │ ├── StringTransformer.js │ │ ├── Transforms.js │ │ ├── inspect.js │ │ ├── normalizeFSPath.js │ │ └── streamToString.js │ ├── resolve/ │ │ ├── builtins.js │ │ ├── getRequireRemapFromDir.js │ │ ├── index.js │ │ └── parseRequire.js │ ├── transforms.js │ ├── util/ │ │ ├── CombinedStream.js │ │ ├── DeferredReadable.js │ │ ├── caching-stream.js │ │ ├── fingerprint-stream.js │ │ ├── hash.js │ │ ├── index.js │ │ ├── prebuild.js │ │ ├── stringify-attrs.js │ │ └── url-reader.js │ └── writers/ │ ├── Writer.js │ ├── file-writer.js │ └── index.js ├── test/ │ ├── .eslintrc │ ├── .gitignore │ ├── api-test.js │ ├── autotest.js │ ├── autotests/ │ │ ├── api/ │ │ │ ├── config-fingerprint/ │ │ │ │ ├── foo.js │ │ │ │ └── test.js │ │ │ ├── lasso-lassoPage-promise/ │ │ │ │ ├── browser.json │ │ │ │ ├── foo.js │ │ │ │ └── test.js │ │ │ ├── lasso-lassoResource-buffer/ │ │ │ │ └── test.js │ │ │ ├── lasso-lassoResource-buffer-name/ │ │ │ │ └── test.js │ │ │ ├── lasso-lassoResource-promise/ │ │ │ │ ├── foo.txt │ │ │ │ └── test.js │ │ │ ├── myLasso-lassoPage-promise/ │ │ │ │ ├── browser.json │ │ │ │ ├── foo.js │ │ │ │ └── test.js │ │ │ ├── myLasso-lassoResource-promise/ │ │ │ │ ├── foo.txt │ │ │ │ └── test.js │ │ │ └── pollute-default-config/ │ │ │ └── test.js │ │ ├── bundling/ │ │ │ ├── async-dependencies/ │ │ │ │ ├── a.js │ │ │ │ ├── b.js │ │ │ │ ├── browser.json │ │ │ │ ├── foo-async.js │ │ │ │ ├── foo-something-else.js │ │ │ │ ├── foo.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── async-flags/ │ │ │ │ ├── DO_NOT_INCLUDE_ME.js │ │ │ │ ├── browser.json │ │ │ │ ├── foo.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── async-package/ │ │ │ │ ├── bar.js │ │ │ │ ├── browser.json │ │ │ │ ├── foo.js │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── bundle-getImageInfo-skip/ │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── bundles/ │ │ │ │ ├── a.js │ │ │ │ ├── b.js │ │ │ │ ├── browser.json │ │ │ │ ├── c.js │ │ │ │ ├── d.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── bundling-strategy-default/ │ │ │ │ ├── a.js │ │ │ │ ├── b.js │ │ │ │ ├── browser.json │ │ │ │ ├── c.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── bundling-strategy-lean/ │ │ │ │ ├── a.js │ │ │ │ ├── b.js │ │ │ │ ├── browser.json │ │ │ │ ├── c.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── bundling-virtual-module/ │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── csp-nonce-inline-script/ │ │ │ │ ├── a.js │ │ │ │ ├── b.js │ │ │ │ ├── browser.json │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── css-inline-resource-base64/ │ │ │ │ ├── expected.css │ │ │ │ ├── foo.css │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── css-inline-resource-utf8/ │ │ │ │ ├── expected.css │ │ │ │ ├── foo.css │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── css-resources-multiple-pages-no-bundling/ │ │ │ │ ├── foo.css │ │ │ │ ├── foo.resource │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── custom-dependency-type-no-bundling/ │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── dedupe/ │ │ │ │ ├── a.js │ │ │ │ ├── b.js │ │ │ │ ├── browser.json │ │ │ │ ├── c.js │ │ │ │ ├── d.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── dependency-code/ │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── escape-external-css-url/ │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── escape-external-js-url/ │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── external-js/ │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── external-js-inlined/ │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── external-js-integrity/ │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── fingerprints/ │ │ │ │ ├── browser.json │ │ │ │ ├── foo.css │ │ │ │ ├── foo.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── glob-patterns/ │ │ │ │ ├── browser.json │ │ │ │ ├── package.json │ │ │ │ ├── require/ │ │ │ │ │ ├── bar.js │ │ │ │ │ └── foo.js │ │ │ │ ├── style/ │ │ │ │ │ ├── style1.css │ │ │ │ │ └── style2.css │ │ │ │ └── test.js │ │ │ ├── glob-patterns-relative-paths/ │ │ │ │ ├── browser.json │ │ │ │ ├── package.json │ │ │ │ ├── require/ │ │ │ │ │ ├── bar.js │ │ │ │ │ └── foo.js │ │ │ │ ├── style/ │ │ │ │ │ ├── style1.css │ │ │ │ │ └── style2.css │ │ │ │ └── test.js │ │ │ ├── inline-async-script/ │ │ │ │ ├── a.js │ │ │ │ ├── b.js │ │ │ │ ├── browser.json │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── inline-css-html-chars/ │ │ │ │ ├── expected.html │ │ │ │ ├── package.json │ │ │ │ ├── style.css │ │ │ │ └── test.js │ │ │ ├── inline-defer-script/ │ │ │ │ ├── a.js │ │ │ │ ├── b.js │ │ │ │ ├── browser.json │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── inline-script-external-attrs/ │ │ │ │ ├── a.js │ │ │ │ ├── b.js │ │ │ │ ├── browser.json │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── intersection/ │ │ │ │ ├── bar.js │ │ │ │ ├── common.js │ │ │ │ ├── foo.js │ │ │ │ ├── package.json │ │ │ │ ├── page1.browser.json │ │ │ │ ├── page2.browser.json │ │ │ │ └── test.js │ │ │ ├── intersection-threshold-100percent/ │ │ │ │ ├── a.browser.json │ │ │ │ ├── a.js │ │ │ │ ├── ab.browser.json │ │ │ │ ├── abc.browser.json │ │ │ │ ├── b.js │ │ │ │ ├── bc.browser.json │ │ │ │ ├── c.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── intersection-threshold-2/ │ │ │ │ ├── a.browser.json │ │ │ │ ├── a.js │ │ │ │ ├── ab.browser.json │ │ │ │ ├── abc.browser.json │ │ │ │ ├── b.js │ │ │ │ ├── bc.browser.json │ │ │ │ ├── c.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── intersection-threshold-2-require/ │ │ │ │ ├── a.js │ │ │ │ ├── b.js │ │ │ │ ├── main.browser.json │ │ │ │ ├── package.json │ │ │ │ ├── shared.js │ │ │ │ └── test.js │ │ │ ├── intersection-threshold-2str/ │ │ │ │ ├── a.browser.json │ │ │ │ ├── a.js │ │ │ │ ├── ab.browser.json │ │ │ │ ├── abc.browser.json │ │ │ │ ├── b.js │ │ │ │ ├── bc.browser.json │ │ │ │ ├── c.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── intersection-threshold-3/ │ │ │ │ ├── a.browser.json │ │ │ │ ├── a.js │ │ │ │ ├── ab.browser.json │ │ │ │ ├── abc.browser.json │ │ │ │ ├── b.js │ │ │ │ ├── bc.browser.json │ │ │ │ ├── c.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── intersection-threshold-50percent/ │ │ │ │ ├── a.browser.json │ │ │ │ ├── a.js │ │ │ │ ├── ab.browser.json │ │ │ │ ├── abc.browser.json │ │ │ │ ├── b.js │ │ │ │ ├── bc.browser.json │ │ │ │ ├── c.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── modules-ready-lastSlot/ │ │ │ │ ├── body.js │ │ │ │ ├── head.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── no-bundles/ │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── recurseInto-all/ │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── recurseInto-dir/ │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── recurseInto-dirtree/ │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── recurseInto-module/ │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── registerRequireExtension-not-cacheable/ │ │ │ │ ├── foo.js │ │ │ │ ├── hello.dynamic │ │ │ │ ├── hello.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── resource-absolute-path/ │ │ │ │ ├── foo.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── slots/ │ │ │ │ ├── foo.css │ │ │ │ ├── foo.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── slots-inline-beginning/ │ │ │ │ ├── a.js │ │ │ │ ├── b.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── slots-inline-end/ │ │ │ │ ├── a.js │ │ │ │ ├── b.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── slots-inline-false/ │ │ │ │ ├── a.js │ │ │ │ ├── b.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── slots-inline-in-place/ │ │ │ │ ├── a.js │ │ │ │ ├── b.js │ │ │ │ ├── c.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── slots-inline-true/ │ │ │ │ ├── a.js │ │ │ │ ├── b.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ └── slots-override/ │ │ │ ├── foo.css │ │ │ ├── foo.js │ │ │ ├── package.json │ │ │ └── test.js │ │ ├── dep-require/ │ │ │ ├── async/ │ │ │ │ ├── bar.js │ │ │ │ ├── expected.json │ │ │ │ ├── foo.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── builtin/ │ │ │ │ ├── expected.json │ │ │ │ ├── foo.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── globals-custom/ │ │ │ │ ├── expected.json │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── globals-jquery/ │ │ │ │ ├── expected.json │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── main-custom/ │ │ │ │ ├── bar/ │ │ │ │ │ ├── bar.js │ │ │ │ │ └── package.json │ │ │ │ ├── expected.json │ │ │ │ ├── foo.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── main-index/ │ │ │ │ ├── bar/ │ │ │ │ │ └── index.js │ │ │ │ ├── expected.json │ │ │ │ ├── foo.js │ │ │ │ ├── module-no-dependencies/ │ │ │ │ │ ├── expected.json │ │ │ │ │ ├── foo.js │ │ │ │ │ ├── package.json │ │ │ │ │ └── test.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── main-require-redefined/ │ │ │ │ ├── bar/ │ │ │ │ │ ├── bar.js │ │ │ │ │ └── package.json │ │ │ │ ├── expected.json │ │ │ │ ├── foo.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── remap-browser-override/ │ │ │ │ ├── expected.json │ │ │ │ ├── foo-browser.js │ │ │ │ ├── foo.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── require-installed/ │ │ │ │ ├── expected.json │ │ │ │ ├── foo.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── require-installed-scoped/ │ │ │ │ ├── expected.json │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── require-search-path/ │ │ │ │ ├── app-modules/ │ │ │ │ │ └── bar/ │ │ │ │ │ ├── index.js │ │ │ │ │ └── package.json │ │ │ │ ├── expected.json │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── transitive/ │ │ │ │ ├── bar.js │ │ │ │ ├── expected.json │ │ │ │ ├── foo.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── transitive-resolved/ │ │ │ │ ├── bar.js │ │ │ │ ├── expected.json │ │ │ │ ├── foo.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ └── virtual-module/ │ │ │ ├── expected.json │ │ │ ├── package.json │ │ │ └── test.js │ │ ├── dep-transport-define/ │ │ │ ├── async/ │ │ │ │ ├── bar.js │ │ │ │ ├── expected.js │ │ │ │ ├── foo.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── builtin/ │ │ │ │ ├── expected.js │ │ │ │ ├── foo.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── globals-foo/ │ │ │ │ ├── expected.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── globals-jquery/ │ │ │ │ ├── expected.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── main-custom/ │ │ │ │ ├── bar/ │ │ │ │ │ ├── bar.js │ │ │ │ │ └── package.json │ │ │ │ ├── expected.js │ │ │ │ ├── foo.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── main-index/ │ │ │ │ ├── bar/ │ │ │ │ │ └── index.js │ │ │ │ ├── expected.js │ │ │ │ ├── foo.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── module-no-dependencies/ │ │ │ │ ├── expected.js │ │ │ │ ├── foo.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── remap-void/ │ │ │ │ ├── bar.js │ │ │ │ ├── expected.js │ │ │ │ ├── foo.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── require-installed/ │ │ │ │ ├── expected.js │ │ │ │ ├── foo.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── require-installed-scoped/ │ │ │ │ ├── expected.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── require-search-path/ │ │ │ │ ├── app-modules/ │ │ │ │ │ └── bar/ │ │ │ │ │ ├── index.js │ │ │ │ │ └── package.json │ │ │ │ ├── expected.js │ │ │ │ ├── foo.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── transform-browserify/ │ │ │ │ ├── bar.js │ │ │ │ ├── expected.js │ │ │ │ ├── my-transform.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── transform-browserify-config/ │ │ │ │ ├── bar.js │ │ │ │ ├── expected.js │ │ │ │ ├── my-transform.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── transform-multiple/ │ │ │ │ ├── bar.js │ │ │ │ ├── expected.js │ │ │ │ ├── package.json │ │ │ │ ├── test.js │ │ │ │ ├── transform-a.js │ │ │ │ ├── transform-b.js │ │ │ │ └── transform-c.js │ │ │ ├── transform-promise/ │ │ │ │ ├── bar.js │ │ │ │ ├── expected.js │ │ │ │ ├── my-transform.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── transform-stream/ │ │ │ │ ├── bar.js │ │ │ │ ├── expected.js │ │ │ │ ├── my-transform.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── transform-stream-config/ │ │ │ │ ├── bar.js │ │ │ │ ├── expected.js │ │ │ │ ├── my-transform.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── transform-stream-no-config/ │ │ │ │ ├── bar.js │ │ │ │ ├── expected.js │ │ │ │ ├── my-transform.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── transform-synchronous/ │ │ │ │ ├── bar.js │ │ │ │ ├── expected.js │ │ │ │ ├── my-transform.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── transform-synchronous-config/ │ │ │ │ ├── bar.js │ │ │ │ ├── expected.js │ │ │ │ ├── my-transform.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── transitive/ │ │ │ │ ├── bar.js │ │ │ │ ├── expected.js │ │ │ │ ├── foo.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ └── transitive-resolved/ │ │ │ ├── bar.js │ │ │ ├── expected.js │ │ │ ├── package.json │ │ │ └── test.js │ │ ├── dep-transport-installed/ │ │ │ └── simple/ │ │ │ ├── expected.js │ │ │ ├── package.json │ │ │ └── test.js │ │ ├── dep-transport-main/ │ │ │ └── simple/ │ │ │ ├── expected.js │ │ │ ├── package.json │ │ │ └── test.js │ │ ├── dep-transport-ready/ │ │ │ └── simple/ │ │ │ ├── expected.js │ │ │ ├── package.json │ │ │ └── test.js │ │ ├── dep-transport-remap/ │ │ │ └── simple/ │ │ │ ├── expected.js │ │ │ ├── package.json │ │ │ └── test.js │ │ ├── dep-transport-run/ │ │ │ ├── no-wait/ │ │ │ │ ├── expected.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ └── wait/ │ │ │ ├── expected.js │ │ │ ├── package.json │ │ │ └── test.js │ │ ├── dependency-walker/ │ │ │ ├── flat/ │ │ │ │ ├── browser.json │ │ │ │ ├── expected.txt │ │ │ │ ├── foo.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── globs/ │ │ │ │ ├── browser.json │ │ │ │ ├── expected.txt │ │ │ │ ├── package.json │ │ │ │ ├── require/ │ │ │ │ │ ├── bar.js │ │ │ │ │ └── foo.js │ │ │ │ ├── style/ │ │ │ │ │ ├── style1.css │ │ │ │ │ └── style2.css │ │ │ │ └── test.js │ │ │ ├── lasso-issue-136/ │ │ │ │ ├── browser.json │ │ │ │ ├── common.js │ │ │ │ ├── expected.txt │ │ │ │ ├── foo1.js │ │ │ │ ├── foo2.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── require/ │ │ │ │ ├── browser.json │ │ │ │ ├── expected.txt │ │ │ │ ├── foo.js │ │ │ │ ├── nested/ │ │ │ │ │ └── index.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── require-mixed/ │ │ │ │ ├── browser.json │ │ │ │ ├── expected.txt │ │ │ │ ├── foo.js │ │ │ │ ├── hello.css │ │ │ │ ├── hello.js │ │ │ │ ├── nested/ │ │ │ │ │ └── index.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ └── transitive-package/ │ │ │ ├── browser.json │ │ │ ├── expected.txt │ │ │ ├── foo.js │ │ │ ├── nested/ │ │ │ │ ├── bar.js │ │ │ │ └── browser.json │ │ │ ├── package.json │ │ │ └── test.js │ │ ├── flags/ │ │ │ ├── bundling-enabled/ │ │ │ │ ├── a.js │ │ │ │ ├── b.js │ │ │ │ ├── browser.json │ │ │ │ ├── c.js │ │ │ │ ├── desktop-a.js │ │ │ │ ├── desktop-b.js │ │ │ │ ├── mobile-a.js │ │ │ │ ├── mobile-b.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── if-flag-dependencies/ │ │ │ │ ├── a.js │ │ │ │ ├── b.js │ │ │ │ ├── browser.json │ │ │ │ ├── c.js │ │ │ │ ├── desktop-a.js │ │ │ │ ├── desktop-b.js │ │ │ │ ├── mobile-a.js │ │ │ │ ├── mobile-b.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── if-not-flag/ │ │ │ │ ├── a.js │ │ │ │ ├── b.js │ │ │ │ ├── browser.json │ │ │ │ ├── c.js │ │ │ │ ├── desktop-a.js │ │ │ │ ├── desktop-b.js │ │ │ │ ├── mobile-a.js │ │ │ │ ├── mobile-b.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── long-flags/ │ │ │ │ ├── a.js │ │ │ │ ├── browser.json │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ └── simple/ │ │ │ ├── a.js │ │ │ ├── b.js │ │ │ ├── browser.json │ │ │ ├── c.js │ │ │ ├── desktop-a.js │ │ │ ├── desktop-b.js │ │ │ ├── mobile-a.js │ │ │ ├── mobile-b.js │ │ │ ├── package.json │ │ │ └── test.js │ │ ├── inspect/ │ │ │ ├── buffer/ │ │ │ │ ├── expected.json │ │ │ │ └── input.js │ │ │ ├── complex/ │ │ │ │ ├── expected.json │ │ │ │ └── input.js │ │ │ ├── first-mate/ │ │ │ │ ├── expected.json │ │ │ │ └── input.js │ │ │ ├── global-late-def/ │ │ │ │ ├── expected.json │ │ │ │ └── input.js │ │ │ ├── globals/ │ │ │ │ ├── expected.json │ │ │ │ └── input.js │ │ │ ├── lasso-loader-var/ │ │ │ │ ├── expected.json │ │ │ │ └── input.js │ │ │ ├── parse-error/ │ │ │ │ ├── expected.json │ │ │ │ └── input.js │ │ │ ├── process1/ │ │ │ │ ├── expected.json │ │ │ │ └── input.js │ │ │ ├── process2/ │ │ │ │ ├── expected.json │ │ │ │ └── input.js │ │ │ ├── process3/ │ │ │ │ ├── expected.json │ │ │ │ └── input.js │ │ │ ├── process4/ │ │ │ │ ├── expected.json │ │ │ │ └── input.js │ │ │ └── simple/ │ │ │ ├── expected.json │ │ │ └── input.js │ │ ├── load-prebuild/ │ │ │ ├── error-invalid-path/ │ │ │ │ └── test.js │ │ │ ├── error-no-build/ │ │ │ │ ├── page.prebuild.json │ │ │ │ └── test.js │ │ │ ├── load-valid-prebuild/ │ │ │ │ ├── page.prebuild.json │ │ │ │ └── test.js │ │ │ ├── load-valid-prebuild-multi-flags/ │ │ │ │ ├── page.prebuild.json │ │ │ │ └── test.js │ │ │ └── load-valid-prebuild-multi-no-flags/ │ │ │ ├── page.prebuild.json │ │ │ └── test.js │ │ ├── modules/ │ │ │ ├── async/ │ │ │ │ ├── foo.js │ │ │ │ ├── lasso-loader-patch.js │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── async-flags/ │ │ │ │ ├── foo.js │ │ │ │ ├── lasso-loader-patch.js │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── async-intersection/ │ │ │ │ ├── foo.js │ │ │ │ ├── lasso-loader-patch.js │ │ │ │ ├── main1-helper.js │ │ │ │ ├── main1.js │ │ │ │ ├── main2-helper.js │ │ │ │ ├── main2.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── async-package/ │ │ │ │ ├── bar.js │ │ │ │ ├── browser.json │ │ │ │ ├── foo.js │ │ │ │ ├── lasso-loader-patch.js │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ ├── something.js │ │ │ │ └── test.js │ │ │ ├── async-package-css/ │ │ │ │ ├── browser.json │ │ │ │ ├── lasso-loader-patch.js │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ ├── something.css │ │ │ │ └── test.js │ │ │ ├── async-raptor-loader/ │ │ │ │ ├── foo.js │ │ │ │ ├── lasso-loader-patch.js │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── async-unnecessary/ │ │ │ │ ├── foo.js │ │ │ │ ├── lasso-loader-patch.js │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── browser.json-tilde/ │ │ │ │ ├── browser.json │ │ │ │ ├── nested/ │ │ │ │ │ ├── browser.json │ │ │ │ │ └── nested.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── browserify-transform/ │ │ │ │ ├── foo-transform.js │ │ │ │ ├── foo.js │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── browserify-transforms/ │ │ │ │ ├── bar-transform.js │ │ │ │ ├── foo-transform.js │ │ │ │ ├── foo.js │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── duplicate-name-browser-json/ │ │ │ │ ├── bar/ │ │ │ │ │ ├── browser.json │ │ │ │ │ └── hello.js │ │ │ │ ├── browser.json │ │ │ │ ├── foo/ │ │ │ │ │ ├── browser.json │ │ │ │ │ └── hello.js │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── mask-define-vanilla-js/ │ │ │ │ ├── define-global.js │ │ │ │ ├── library.js │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── missing-module/ │ │ │ │ ├── a.js │ │ │ │ ├── b.js │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── no-conflict/ │ │ │ │ ├── foo.js │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── registerRequireExtension-getDependencies/ │ │ │ │ ├── extra.js │ │ │ │ ├── hello.foo │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ ├── test.js │ │ │ │ └── world.foo │ │ │ ├── registerRequireType/ │ │ │ │ ├── hello.foo │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ ├── test.js │ │ │ │ └── world.foo │ │ │ ├── registerRequireType-getDependencies-callback/ │ │ │ │ ├── extra.js │ │ │ │ ├── hello.foo │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ ├── test.js │ │ │ │ └── world.foo │ │ │ ├── registerRequireType-getDependencies-promise/ │ │ │ │ ├── extra.js │ │ │ │ ├── hello.foo │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ ├── test.js │ │ │ │ └── world.foo │ │ │ ├── registerRequireType-getDependencies-value/ │ │ │ │ ├── extra.js │ │ │ │ ├── hello.foo │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ ├── test.js │ │ │ │ └── world.foo │ │ │ ├── require-builtin-core/ │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── require-css/ │ │ │ │ ├── foo.css │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── require-custom-ext/ │ │ │ │ ├── hello.foo │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ ├── require-foo-plugin.js │ │ │ │ └── test.js │ │ │ ├── require-custom-ext-no-plugin/ │ │ │ │ ├── hello.foo │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── require-custom-type/ │ │ │ │ ├── foo.json │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── require-globals/ │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── require-installed/ │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── require-installed-scoped-package/ │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── require-json/ │ │ │ │ ├── foo.json │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── require-relative/ │ │ │ │ ├── foo.js │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── require-remap-flag/ │ │ │ │ ├── bar-desktop.js │ │ │ │ ├── bar.js │ │ │ │ ├── browser.json │ │ │ │ ├── foo-mobile.js │ │ │ │ ├── foo.js │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── require-remap-flag-caching/ │ │ │ │ ├── bar-desktop.js │ │ │ │ ├── bar.js │ │ │ │ ├── browser.json │ │ │ │ ├── foo-mobile.js │ │ │ │ ├── foo.js │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── require-remap-local-to-installed/ │ │ │ │ ├── foo.js │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── require-remap-local-to-local/ │ │ │ │ ├── foo-browser.js │ │ │ │ ├── foo.js │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── require-remap-void-installed/ │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── require-remap-void-relative/ │ │ │ │ ├── foo.js │ │ │ │ ├── foo2.js │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── require-resolve/ │ │ │ │ ├── foo.js │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── require-virtual-module/ │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── requireRemap-not-conditional/ │ │ │ │ ├── browser.json │ │ │ │ ├── foo-browser.js │ │ │ │ ├── foo.js │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── requireRemap-plus-package-browser/ │ │ │ │ ├── browser.json │ │ │ │ ├── foo-browser-lasso.js │ │ │ │ ├── foo-browser.js │ │ │ │ ├── foo.js │ │ │ │ ├── main.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ └── simple/ │ │ │ ├── main.js │ │ │ ├── package.json │ │ │ └── test.js │ │ ├── plugins/ │ │ │ ├── custom-dependency-type-callback/ │ │ │ │ ├── browser.json │ │ │ │ ├── foo.css │ │ │ │ ├── foo.js │ │ │ │ ├── package.json │ │ │ │ ├── plugin.js │ │ │ │ └── test.js │ │ │ ├── custom-dependency-type-promise/ │ │ │ │ ├── browser.json │ │ │ │ ├── foo.css │ │ │ │ ├── foo.js │ │ │ │ ├── package.json │ │ │ │ ├── plugin.js │ │ │ │ └── test.js │ │ │ ├── events/ │ │ │ │ ├── async-foo.js │ │ │ │ ├── browser.json │ │ │ │ ├── expected-events.json │ │ │ │ ├── foo.js │ │ │ │ ├── package.json │ │ │ │ ├── plugin.js │ │ │ │ └── test.js │ │ │ ├── global-dependency-prop/ │ │ │ │ ├── package.json │ │ │ │ ├── plugin.js │ │ │ │ ├── something.foo │ │ │ │ └── test.js │ │ │ ├── inline-all-for-slot/ │ │ │ │ ├── bar.js │ │ │ │ ├── baz.js │ │ │ │ ├── browser.json │ │ │ │ ├── expected-my-inline-slot.html │ │ │ │ ├── foo.js │ │ │ │ ├── package.json │ │ │ │ ├── plugin.js │ │ │ │ └── test.js │ │ │ ├── lasso-context-event-bundle-written/ │ │ │ │ ├── browser.json │ │ │ │ ├── expected-events.json │ │ │ │ ├── foo.js │ │ │ │ ├── package.json │ │ │ │ ├── plugin.js │ │ │ │ └── test.js │ │ │ ├── lasso-context-event-resource-written/ │ │ │ │ ├── browser.json │ │ │ │ ├── expected-events.json │ │ │ │ ├── fonts/ │ │ │ │ │ └── fonts.css │ │ │ │ ├── package.json │ │ │ │ ├── plugin.js │ │ │ │ └── test.js │ │ │ ├── lasso-writer-event-bundle-written/ │ │ │ │ ├── browser.json │ │ │ │ ├── expected-events.json │ │ │ │ ├── foo.js │ │ │ │ ├── package.json │ │ │ │ ├── plugin.js │ │ │ │ └── test.js │ │ │ ├── lasso-writer-event-resource-written/ │ │ │ │ ├── browser.json │ │ │ │ ├── expected-events.json │ │ │ │ ├── fonts/ │ │ │ │ │ └── fonts.css │ │ │ │ ├── package.json │ │ │ │ ├── plugin.js │ │ │ │ └── test.js │ │ │ ├── registerRequireExtension/ │ │ │ │ ├── package.json │ │ │ │ ├── plugin.js │ │ │ │ ├── something.foo │ │ │ │ └── test.js │ │ │ ├── registerRequireType/ │ │ │ │ ├── browser.json │ │ │ │ ├── package.json │ │ │ │ ├── plugin.js │ │ │ │ ├── something.foo │ │ │ │ └── test.js │ │ │ └── write-bundle/ │ │ │ ├── browser.json │ │ │ ├── foo.js │ │ │ ├── package.json │ │ │ ├── plugin.js │ │ │ └── test.js │ │ ├── prebuild-page/ │ │ │ ├── invalid-prebuild-config/ │ │ │ │ └── test.js │ │ │ ├── multi-prebuild/ │ │ │ │ ├── a.js │ │ │ │ ├── b.js │ │ │ │ ├── page.prebuild.expected.json │ │ │ │ ├── page1.prebuild.expected.json │ │ │ │ └── test.js │ │ │ ├── prebuild-resource/ │ │ │ │ ├── test-page.prebuild.expected.json │ │ │ │ └── test.js │ │ │ └── valid-prebuild/ │ │ │ ├── a.js │ │ │ ├── test-page-1.prebuild.expected.json │ │ │ └── test.js │ │ ├── require-no-op/ │ │ │ ├── enable/ │ │ │ │ ├── test.js │ │ │ │ ├── test.xbar │ │ │ │ ├── test.xfoo │ │ │ │ ├── test.ybar │ │ │ │ └── test.yfoo │ │ │ ├── enable-ignore-null-extension/ │ │ │ │ ├── test.js │ │ │ │ ├── test.xbar │ │ │ │ ├── test.xfoo │ │ │ │ ├── test.ybar │ │ │ │ └── test.yfoo │ │ │ └── enable-throw-invalid-extension-type/ │ │ │ ├── test.js │ │ │ ├── test.xbar │ │ │ ├── test.xfoo │ │ │ ├── test.ybar │ │ │ └── test.yfoo │ │ ├── resource-transforms/ │ │ │ ├── filter/ │ │ │ │ ├── package.json │ │ │ │ ├── test.js │ │ │ │ ├── transform.bar │ │ │ │ └── transform.foo │ │ │ └── stream/ │ │ │ ├── package.json │ │ │ ├── test.js │ │ │ ├── transform.bar │ │ │ └── transform.foo │ │ ├── transforms/ │ │ │ ├── minify-js/ │ │ │ │ ├── browser.json │ │ │ │ ├── foo.js │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── output-transforms/ │ │ │ │ ├── browser.json │ │ │ │ ├── css-transform1.js │ │ │ │ ├── css-transform2.js │ │ │ │ ├── js-transform1-async.js │ │ │ │ ├── js-transform2-async.js │ │ │ │ ├── package.json │ │ │ │ ├── test.js │ │ │ │ ├── transformsA.css │ │ │ │ └── transformsA.js │ │ │ ├── resolve-font-urls/ │ │ │ │ ├── browser.json │ │ │ │ ├── fonts/ │ │ │ │ │ └── fonts.css │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── transform-css-urls-custom-resolver/ │ │ │ │ ├── browser.json │ │ │ │ ├── foo.css │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── transform-css-urls-custom-type/ │ │ │ │ ├── browser.json │ │ │ │ ├── foo.css │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── transform-css-urls-fingerprints/ │ │ │ │ ├── browser.json │ │ │ │ ├── foo.css │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── transform-css-urls-no-bundling/ │ │ │ │ ├── browser.json │ │ │ │ ├── foo.css │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── transform-css-urls-no-fingerprints/ │ │ │ │ ├── browser.json │ │ │ │ ├── foo.css │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ ├── transform-css-urls-no-relative/ │ │ │ │ ├── browser.json │ │ │ │ ├── foo.css │ │ │ │ ├── package.json │ │ │ │ └── test.js │ │ │ └── transform-css-urls-require/ │ │ │ ├── browser.json │ │ │ ├── foo.css │ │ │ ├── package.json │ │ │ └── test.js │ │ └── util/ │ │ └── caching-replay-stream/ │ │ ├── .gitignore │ │ ├── hello.txt │ │ └── test.js │ ├── builtins-test.js │ ├── bundling-test.js │ ├── dep-require-test.js │ ├── dep-transport-define-test.js │ ├── dep-transport-installed-test.js │ ├── dep-transport-main-test.js │ ├── dep-transport-ready-test.js │ ├── dep-transport-remap-test.js │ ├── dep-transport-run-test.js │ ├── dependency-walker-test.js │ ├── fixtures/ │ │ ├── builtin-foo.js │ │ ├── file.json │ │ ├── foo-shim.js │ │ └── inspect-cache/ │ │ ├── bar.js │ │ └── foo.js │ ├── flags-test.js │ ├── inspect-cache-test.js │ ├── inspect-test.js │ ├── lasso-image-test.js │ ├── load-prebuild-test.js │ ├── mock/ │ │ ├── LassoManifest.js │ │ ├── MockDependency.js │ │ ├── MockLassoContext.js │ │ ├── MockMemoryCache.js │ │ ├── MockRequireHandler.js │ │ ├── condition.js │ │ ├── create-lasso-context.js │ │ ├── dependency-factory.js │ │ ├── fingerprint-stream.js │ │ ├── manifest-loader.js │ │ └── mock-lasso.js │ ├── modules-test.js │ ├── plugins-test.js │ ├── prebuild-page-test.js │ ├── require-no-op-test.js │ ├── resource-transforms-test.js │ ├── transforms-test.js │ ├── unit/ │ │ ├── AsyncPackage-test.js │ │ ├── LassoPageResult-test.js │ │ └── hash-test.js │ ├── util/ │ │ ├── WriterTracker.js │ │ ├── index.js │ │ ├── module-search-path.js │ │ ├── normalizeOutput.js │ │ ├── patch-module.js │ │ └── test-init.js │ └── util-test.js └── test-loop.sh ================================================ FILE CONTENTS ================================================ ================================================ FILE: .editorconfig ================================================ # top-most EditorConfig file root = true # Unix-style newlines with a newline ending every file [*] end_of_line = lf insert_final_newline = true # Matches multiple files with brace expansion notation # Set default charset [*.{js,json,marko,yml}] charset = utf-8 indent_style = space indent_size = 4 ================================================ FILE: .eslintrc ================================================ { "extends": "standard", "rules": { "indent": ["error", 4], "semi": ["error", "always"], "space-before-function-paren": ["off"], "no-path-concat": ["off"], "no-useless-escape": ["off"], "no-eval": ["off"] } } ================================================ FILE: .gitignore ================================================ /work /build /.idea/ /npm-debug.log /node_modules /*.sublime-workspace *.orig .DS_Store coverage /test/build /test/static *.actual.js *.actual.html .lasso-cache .cache ~* .nyc_output/ .vscode test/autotests/prebuild-page/*/static test/autotests/prebuild-page/*/*.prebuild.json ================================================ FILE: .npmignore ================================================ /work /build /.idea/ /npm-debug.log /node_modules /*.sublime-workspace *.orig .DS_Store coverage /test/build *.actual.js *.actual.html .lasso-cache .cache /test *.marko.js *.dust.js .nyc_output/ coverage/ ================================================ FILE: .travis.yml ================================================ node_js: - 10 - 8 - 6 sudo: false language: node_js # Use latest version of npm before_install: npm install -g npm@* script: "npm test" ================================================ FILE: CHANGELOG.md ================================================ Changelog ========= # 4.0.3 - Fix issue where the internal lasso context data was missing for image/resource assets. # 4.0.2 - Fix issue with caching page results in production mode. # 4.0.1 - Fix regression with the `getImageInfo` api. # 4.0.0 - **BREAKING**: Requires a minimum of node 16 - Upgrades internal dependencies (biggest user facing change being JS/CSS parsing and minification of newer features). # 3.4.0 - Switches from esprima to espree to parse JavaScript (supports more modern JavaScript). # 3.3.1 - Adds `.webp` to the list of browser-refresh extensions. # 3.3.0 - use [terser](https://github.com/terser-js/terser) instead uglify-js because it's [no longer maintained](https://github.com/mishoo/UglifyJS2/issues/3156#issuecomment-392943058). # 3.2.8 - Fix regression with inline scripts with `externalScriptAttrs`. # 3.2.7 - Improve support for `defer` and `async` attributes on inline bundles. # 3.0.0 - **BREAKING**: Remove support for Node 4 - Significant refactors. Move to async/await internally. - Traspile for Node 4 support using Babel - **BREAKING**: API methods no longer expose callbacks. - Plugins should no longer expect a callback - Plugins should return promises for async tasks - **BREAKING**: Marko taglib and taglib-v2 removed - Use `@lasso/marko-taglib` instead - Support for passing `cacheKey` property to lasso config - Merge `lasso-require` into Lasso - Lasso config `lassoConfig.require.resolver` property has been split out into its own property `lassoConfig.resolver` ```js const lassoConfig: { require: { transforms: ... }, resolver: { builtins: { ... } } } const lasso = lasso.create(lassoConfig); ``` - Lasso writers now support async `init` functions ```js module.exports = function(lasso, config) { lasso.config.writer = { async init (lassoContext) { await Promise.resolve(); }, async writeBundle (reader, lassoContext) { const bundle = lassoContext.bundle; bundle.url = 'test.com'; }, async writeResource (reader, lassoContext) { return { url: 'test.com' }; } }; }; ``` # 2.x ## 2.9.x ### 2.9.0 - Fixes #186 - Allow custom require handler to implement getDependencies ## 2.8.x ### 2.8.5 - Use [resolve-from](https://github.com/sindresorhus/resolve-from) to first try and resolve `browser.json` ### 2.8.4 - Fixes #185 - DependencyRegistry has an undefined stream when using `mask-define` for AMD dependencies - Increasing default timeout to 10s - Other minor internal changes ### 2.8.3 - Internal: use `renderToString` instead of `renderSync` ([PR #182](https://github.com/lasso-js/lasso/pull/182) by [@yomed](https://github.com/yomed)) ### 2.8.2 - Fixed #180 - Defining bundles with "intersection" does not work for "require" dependencies ### 2.8.2 - Fixed #180 - Defining bundles with "intersection" does not work for "require" dependencies ### 2.8.1 - Fixed #178 - Cache key changes when lasso is reconfigured even if config did not change ### 2.8.0 - Fixed #173 - Bundle attributes ## 2.7.x ### 2.7.0 - Restored Marko v2 compatibility ## 2.6.x ### 2.6.1 - Fixes #171 - Write cache key information to disk for debugging purposes ### 2.6.0 - Added support for a new `relativeUrlsEnabled` configuration option ([PR #167](https://github.com/lasso-js/lasso/pull/167) by [@reid](https://github.com/reid)) - Upgraded [glob](https://www.npmjs.com/package/glob) version ([PR #166](https://github.com/lasso-js/lasso/pull/166) by [@yomed](https://github.com/yomed)) - Docs: various improvements to documentation by ([@yomed](https://github.com/yomed)) ## 2.5.x ### 2.5.9 - Introduced Koa-compatible middleware ([PR #163](https://github.com/lasso-js/lasso/pull/163) by [@yomed](https://github.com/yomed)) ### 2.5.8 - Added tests for #160 - lastSlot configuration option for the require plugin ### 2.5.7 - Upgraded to `raptor-util@2.0.0` ### 2.5.6 - Fixed #156 - Lasso is generating very long names in development triggering `ENAMETOOLONG` error [PR #157](https://github.com/lasso-js/lasso/pull/157) from [@mlrawlings](https://github.com/mlrawlings) ### 2.5.5 - Fixes #147 - EPERM 'operation not permitted' on rename - Fixes #148 - css urls resolved incorrectly with multiple pages when bundling disabled - Testing: Don't actually read external URLs when running tests ### 2.5.4 - Fixes #149 - if-flag is ignored in async section of browser.json ### 2.5.3 - Updated taglib type and autocomplete information for tooling - Docs: Added [Michael Rawlings](https://github.com/mlrawlings) as a maintainer ### 2.5.2 - Added `null`/`undefined` when building async loader metadata ### 2.5.1 - Fixed #146 - Slot timeout causes other slots to timeout ### 2.5.0 - Loader metadata for lazily loading packages is no longer stored in a global variable and is instead integrated with the lasso modules client runtime. This change prevents separate lasso builds of JavaScript libraries from conflicting with each other when both added to the same web page. ### 2.4.2 - Code cleanup for base64 encoding ### 2.4.1 - Fixes #143 - Only encode new lines when using utf8 encoding for data uri ### 2.4.0 - Fixes #141 - Add support for UTF8 encoding inline images in CSS files ## 2.3.x ### 2.3.6 - Fixes #137 - Don't allow double callbacks in case of multiple errors on the same read stream ### 2.3.5 - Fixes #136 - Just use a unique ID for packages if calculateKey() is not implemented - Added new events: `beforeAddDependencyToAsyncPageBundle`, `beforeAddDependencyToSyncPageBundle` Example plugin: ```javascript module.exports = exports = function(lasso, config) { lasso.on('beforeBuildPage', (event) => { var context = event.context; context.on('beforeAddDependencyToSyncPageBundle', (event) => { var dependency = event.dependency; }); context.on('beforeAddDependencyToAsyncPageBundle', (event) => { var dependency = event.dependency; }); }); }; ``` ### 2.3.4 - Fixed #135 - Incorrect key is calculated for require dependencies in `browser.json` files in some situations ### 2.3.3 - Fixed #134 - The `` tag breaks bundling when included in a template sent to the browser - Fixed #133 - The dependency chain should be included in error messages when walking a dependency graph ### 2.3.2 - Fixed #130 - check inline minification config value ([@yomed](https://github.com/yomed)) - Updated docs and tests ### 2.3.1 - Minor cleanup ### 2.3.0 - Fixes #84 - Allow minification to only be enabled for inline resources - Switched from `jsonminify` to `strip-json-comments` ## 2.2.x ### 2.2.2 - Fixes #128 - getLastModified is broken when using registerRequireType ### 2.2.1 - JavaScript comments are now stripped before parsing JSON config files ### 2.2.0 - Significant improvements to performance and stability - Resolved issues related to caching and development mode ## 2.1.x ### 2.1.1 - Fixes #116 - Conditional requireRemap is broken ### 2.1.0 - Fixes #115 - Allow conditional dependencies to be grouped ## 2.0.x ### 2.0.1 - `registerRequireType`, switch condition blocks [PR #113](https://github.com/lasso-js/lasso/pull/113) ### 2.0.0 - Marko v3 compatibility # 1.x ## 1.16.x ### 1.16.1 - Adds support for fingerprinting inline code blocks for purpose of creating Content Security Policy (CSP) that secures statically built app. ### 1.16.0 - Added support for custom attributes on script and style tags for slots ## 1.15.x ### 1.15.0 - Adds support for injecting a Content Security Policy nonce into inline script and style tags. Fixes [Issue #93](https://github.com/lasso-js/lasso/issues/93) ## 1.14.x ### 1.14.0 - Fixed #100 - Removed type attributes from ` ``` If you open up `my-page.html` in your web browser you should see a page styled with Less and the output of running `main.js`. Now try again with `production` mode: ```bash lasso style.less \ --main main.js \ --inject-into my-page.html \ --plugins lasso-less \ --production ``` ``` Output for page "my-page": Resource bundle files: static/my-page-2e3e9936.js static/my-page-169ab5d9.css HTML slots file: build/my-page.html.json Updated HTML file: my-page.html ``` The updated `my-page.html` file should be similar to the following: ```html Lasso.js Demo

Lasso.js Demo

``` With the `--production` option enabled, all of the resources are concatenated together, minified and fingerprinted – perfect for high performance web applications running in production. For more documentation on the Command Line Interface please see the [lasso-cli docs](https://github.com/lasso-js/lasso-cli). ## JSON Configuration File
[__Sample App__](https://github.com/lasso-js-samples/lasso-config) To try out and experiment with the code, please see the following project:
[lasso-js-samples/lasso-config](https://github.com/lasso-js-samples/lasso-config)
The number of command line arguments can get unwieldy so it is better to split out configuration into a separate JSON file. Let's now create a configuration file and configure a few bundles to make things more interesting: __lasso-config.json:__ ```json { "plugins": [ "lasso-less" ], "outputDir": "static", "fingerprintsEnabled": true, "minify": true, "resolveCssUrls": true, "bundlingEnabled": true, "bundles": [ { "name": "jquery", "dependencies": [ "require: jquery" ] }, { "name": "math", "dependencies": [ "require: ./add" ] } ] } ``` In addition, let's put our page dependencies in their own JSON file: __my-page.browser.json:__ ```json { "dependencies": [ "./style.less", "require-run: ./main" ] } ``` Now run the page lasso using the newly created JSON config file and JSON dependencies file: ```bash lasso ./my-page.browser.json \ --inject-into my-page.html \ --config lasso-config.json ``` Because of the newly configured bundles, we'll see additional JavaScript bundles written to disk as shown below: ``` Output for page "my-page": Resource bundle files: static/math-169c956d.js static/jquery-24db89d9.js static/my-page-beed0921.js static/my-page-169ab5d9.css HTML slots file: build/my-page.html.json Updated HTML file: my-page.html ``` ## Dependencies Lasso.js walks a dependency graph to find all of the resources that need to be bundled. A dependency can either be a JavaScript or CSS resource (or a file that compiles to either JavaScript or CSS) or a dependency can be a reference to a set of transitive dependencies. Some dependencies are inferred from scanning source code and other dependencies can be made explicit by listing them out in the code of JavaScript modules or in separate `browser.json` files. It's also possible to register your own [custom dependency types](#custom-dependency-types). With custom dependency types, you can control how resources are compiled or a custom dependency type can be used to resolve additional dependencies during optimization. Browser dependencies can be described as shown in the following sample `browser.json` file: ```json { "dependencies": [ "./style.less", "../third-party/jquery.js", "**/*.css", { "type": "js", "url": "https://code.jquery.com/jquery-2.1.4.min.js" }, { "type": "css", "url": "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" } ] } ``` Alternatively, dependencies can be "required" inside a JavaScript module as shown in the following sample JavaScript code: ```javascript require('./style.less'); // ... ``` The only caveat to using a `require()` call to add a non-JavaScript module dependency is that by default Node.js will try to load the required file as a JavaScript module if the code runs on the server. To prevent Node.js from trying to load a Less file or other non-JavaScript files as JavaScript modules you can add the following code to your main script: ```javascript require('lasso/node-require-no-op').enable('.less', '.css'); ``` For simple paths, the dependency type is inferred from the filename extension. Alternatively, the dependency type can be made explicit using either one of the following formats: ```json [ "./style.less", "less: ./style.less", { "type": "less", "path": "./style.less" } ] ``` _NOTE: all of the above are equivalent_ You can also create a dependency that references dependencies in a separate `browser.json` file. Dependencies that have the `browser.json` extension are automatically resolved using the require resolver if they are not relative paths. For example: ```js [ // Relative path: "./some-module/browser.json", // Look for "my-module/browser.json" in "node_modules": "my-module/browser.json" ] ``` If the path does not have a file extension then it is assumed to be a path to an `browser.json` file so the following short-hand works as well: ```js [ "./some-module" "my-module" ] ``` If you use the short-hand notation for `browser.json` dependencies, the paths will still be resolved using the require resolver as long as they are not relative paths. ### External Dependencies Lasso.js does allow referencing external JS/CSS files in your `browser.json` files as shown below: ```json { "dependencies": [ { "type": "js", "url": "https://code.jquery.com/jquery-2.1.4.min.js" }, { "type": "css", "url": "https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" } ] } ``` By default, Lasso.js will not bundle external resources with your application's JavaScript and CSS bundles. If you would prefer for an external resource to be downloaded from the remote server and bundled with your other application code during the lassoing then you can set the `external` property to `false` as shown below (`external` defaults to `true`): ```json { "dependencies": [ { "type": "js", "url": "https://code.jquery.com/jquery-2.1.4.min.js", "external": false } ] } ``` Setting `external` to `false` in the above example will result in jQuery being downloaded from the CDN and bundled with all of the other JS code for the app. That is, the code for jQuery will not be served up by the jQuery CDN. ### Dependency Attributes Adding an `attributes` object to a dependency definition will result in those attributes being defined on the html tag for that dependency. For bundled dependencies, these attributes will be merged with latter dependencies taking priority. The following is an example using the `integrity` and `crossorigin` attributes for [Subresource Integrity (SRI) checking](https://www.w3.org/TR/SRI/). This allows browsers to ensure that resources hosted on third-party servers have not been tampered with. Use of SRI is recommended as a best-practice, whenever libraries are loaded from a third-party source. ```json { "dependencies": [{ "type": "js", "url": "https://code.jquery.com/jquery-3.1.1.min.js", "attributes":{ "integrity":"sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8=", "crossorigin":"anonymous" } }] } ``` **Generated Output:** ```html ``` ### Conditional Dependencies Lasso.js supports conditional dependencies. Conditional dependencies is a powerful feature that allows for a page to be built differently based on certain flags (e.g. "mobile device" versus "desktop"). For caching reasons, the flags for conditional dependencies should be based on a set of enabled flag. A flag is just an arbitrary name that can be enabled/disabled before optimizing a page. For example, to make a dependency conditional such that is only included for mobile devices you can do the following: ```json { "dependencies": [ { "path": "./hello-mobile.js", "if-flag": "mobile" } ] } ``` Alternatively, you can also include the desktop version of a file if the "mobile" extension is not enabled using `if-not-flag`. ```json { "dependencies": [ { "path": "./hello-desktop.js", "if-not-flag": "mobile" } ] } ``` If needed, a JavaScript expression can be used to describe a more complex condition as shown in the following sample code: ```json { "dependencies": [ { "path": "./hello-mobile.js", "if": "flags.contains('phone') || flags.contains('tablet')" } ] } ``` Finally, if you prefer, you can group your conditional dependencies if needed: ```json { "dependencies": [ { "if-flag": "mobile", "dependencies": [ "./style-mobile.css", "./client-mobile.js" ] } ] } ``` ### Enabling Flags The code below shows how to enable flags when optimizing a page: __Using the JavaScript API:__ ```javascript myLasso.lassoPage({ dependencies: [ { path: './hello-mobile.js', 'if-flag': 'mobile' } ], flags: ['mobile', 'foo', 'bar'] }) ``` __Using the Marko taglib:__ ```marko ... ``` ## Asynchronous/Lazy Loading
[__Sample App__](https://github.com/lasso-js-samples/lasso-async)To try out and experiment with the code, please see the following project:
[lasso-js-samples/lasso-async](https://github.com/lasso-js-samples/lasso-async)
Lasso.js supports asynchronously loading dependencies using the lightweight [lasso-loader](https://github.com/lasso-js/lasso-loader) module as shown in the following sample code: ```javascript var lassoLoader = require('lasso-loader'); lassoLoader.async(function(err) { // Any modules that are required within the scope // of this function will be loaded asynchronously*. // Lasso.js ensures that modules are only // loaded once from the server. // // *Modules that were included as part of the initial // page load will automatically be de-duped. if (err) { // Handle the case where one or more of the // dependencies failed to load. } var add = require('./add'); var jquery = require('jquery'); jquery(function() { $(document.body).append('2+2=' + add(2, 2)); }); }); ``` During optimization, Lasso.js detects the call to `require('lasso-loader').async(...)` and transforms the code such that the function is not invoked until all of the required modules referenced in the body of callback function are completely loaded. You can also specify additional explicit dependencies if necessary: ```javascript require('lasso-loader').async( [ './style.less', 'some/other/browser.json' ], function() { // All of the requires nested in this function block will be lazily loaded. // When all of the required resources are loaded then the function will be invoked. var foo = require('foo'); var bar = require('bar'); }); ``` You can also choose to declare async dependencies in an `browser.json` file: ```json { "dependencies": [ ... ], "async": { "my-module/lazy": [ "require: foo", "require: bar", "./style.less", "some/other/browser.json" ] } } ``` The async dependencies can then be referenced in code: ```javascript require('lasso-loader').async( 'my-module/lazy', function() { var foo = require('foo'); var bar = require('bar'); }); ``` ## Using the JavaScript API
[__Sample App__](https://github.com/lasso-js-samples/lasso-js-api) To try out and experiment with the code, please see the following project:
[lasso-js-samples/lasso-js-api](https://github.com/lasso-js-samples/lasso-js-api)
For added flexibility there is a JavaScript API that can be used to lasso pages as shown in the following sample code: ```javascript var lasso = require('lasso'); lasso.configure('lasso-config.json'); lasso.lassoPage({ name: 'my-page', dependencies: [ "./style.less", "require-run: ./main" ] }, function(err, lassoPageResult) { if (err) { // Handle the error } var headHtml = lassoPageResult.getHeadHtml(); // headHtml will contain something similar to the following: // var bodyHtml = lassoPageResult.getBodyHtml(); // bodyHtml will contain something similar to the following: // }); ``` The `lassoPage(options)` method supports the following options: - `data` (`Object`) - Arbitrary data that can be made available to plugins via `lassoContext.data`. - `cacheKey` (`String`) - A unique String to use for cache reads and writes. Defaults to `name`. - `dependencies` (`Array`) - An array of top-level page dependencies (e.g. `['foo.js', 'foo.css', 'require: jquery']`). - `flags` (`Array`) - The set of enabled flags (e.g. `['mobile', 'touch']`). - `from` (`String`) - The base path for resolving relative paths for top-level dependencies. - `name` (`String`) - The page name. Used for determining the names of the output JS/CSS bundles. - `packagePath` (`String`) - The path to an `browser.json` file that describes the top-level dependencies. ### Configuring the Default Lasso ```javascript var lasso = require('lasso'); lasso.configure(config); ``` If the value of the `config` argument is a `String` then it is treated as a path to a JSON configuration file. ### Optimizing a Page The following code illustrates how to lasso a simple set of JavaScript and CSS dependencies using the default configured lasso: ```javascript var lasso = require('lasso'); lasso.lassoPage({ name: 'my-page', dependencies: [ './foo.js', './bar.js', './baz.js', './qux.css' ] }, function(err, lassoPageResult) { if (err) { console.log('Failed to lasso page: ', err); return; } var headHtml = lassoPageResult.getHeadHtml(); /* String with a value similar to the following: */ var bodyHtml = lassoPageResult.getBodyHtml(); /* String with a value similar to the following: */ // Inject the generated HTML into the and sections of a page... }); ``` ### Creating a New Lasso ```javascript var myLasso = lasso.create(config); myLasso.lassoPage(...); ``` ## Lasso.js Taglib
[__Sample App__](https://github.com/lasso-js-samples/lasso-taglib) To try out and experiment with the code, please see the following project:
[lasso-js-samples/lasso-taglib](https://github.com/lasso-js-samples/lasso-taglib)
For the ultimate in usability, a taglib is provided for Marko (and Dust) to automatically lasso a page _and_ inject the required HTML markup to include the JavaScript and CSS bundles. If you are using [Marko](https://github.com/marko-js/marko) you can utilize the available taglib for Lasso.js to easily lasso page dependencies and embed them into your page. ### Using Lasso.js Taglib with Marko 1. `npm install lasso --save` 2. `npm install marko --save` You can now add the lasso tags to your page templates. For example: ```marko Test Page

Test Page

``` You will then need to create an `browser.json` in the same directory as your page template. For example: _browser.json_: ```json { "dependencies": [ "./jquery.js", "./foo.js", "./bar.js", "./style.less" ] } ``` Using Marko and Lasso.js taglib, you can simply render the page using code similar to the following: ```javascript var template = require('marko').load('my-page.marko'); template.render({}, function(err, html) { // html will include all of the required and ``` The lasso result is cached so you can skip the build step! You can also configure the default page lasso used by the lasso tags: ```javascript require('lasso').configure({...}); ``` For more details, please see following documentation: [Lasso.js Taglib for Marko](taglib-marko.md) ### `` The `` tag can be used to render `` tags while also having the image referenced by the `src` attribute automatically go through the Lasso.js asset pipeline. In addition, if the `width` and `height` attributes are not specified then those attributes will automatically be added. This tag can be rendered on both the server and in the browser. Example: ```marko ``` This might produce the following HTML output depending on how Lasso.js is configured: ```html ``` ## Client/Server Template Rendering
[__Sample App__](https://github.com/lasso-js-samples/lasso-templates) To try out and experiment with the code, please see the following project:
[lasso-js-samples/lasso-templates](https://github.com/lasso-js-samples/lasso-templates)
To demonstrate rendering of the same template on the server and the client we will start with the following Marko template: __template.marko__ ```marko -- Hello ${data.name}! ``` _NOTE: The sample app includes sample code that illustrates how to also render both a Dust template and a Handlebars template on both the client and server._ We will then create a `main.js` file to render the template to the console: __main.js:__ ```javascript var template = require('marko') .load(require.resolve('./template.marko')); template.render( { name: 'Frank' }, function(err, html) { console.log('Template output: ' + html); }); ``` _NOTE: The reason we use `require.resolve('./template.marko')` instead of `require('template.marko')` is that Node.js does not understand how to load `.marko` modules and the use of the `require.extensions` has been [deprecated](http://nodejs.org/api/globals.html#globals_require_extensions). `require.resolve()` is used to get the resolved path for the template and the [marko](https://github.com/marko-js/marko) module uses that path to load template into memory._ Running `node main.js` on the server will produce the following output in the console: ```html Template output: Hello Frank! ``` In order to automatically detect and compile required `*.marko` templates we will need to install the [lasso-marko](https://github.com/lasso-js/lasso-marko) plugin and [@lasso/marko-taglib](https://github.com/lasso-js/lasso-marko-taglib) taglib using the following commands: ```bash npm install lasso-marko npm install @lasso/marko-taglib ``` We can then lasso the page using the following command: ```bash lasso style.less \ --main main.js \ --inject-into my-page.html \ --plugins lasso-marko ``` After opening `my-page.html` in your web browser you should then see the same output written to the browser's JavaScript console. ## Middleware for Express and Koa Lasso includes optional middleware for both Express and Koa that can be used to serve up the static files that it generates. ### `serveStatic(options)` The `serveStatic` middleware provided by Lasso is a small wrapper around the [send](https://github.com/pillarjs/send) package. Supported options: - __lasso__ - The configured lasso instance (defaults to `require('lasso').getDefaultLasso()`) - __sendOptions__ - Pass through options for the `send` module. See [send » options](https://github.com/pillarjs/send#optionsd) ### Using `serveStatic` with Express ```javascript app.use(require('lasso/middleware').serveStatic(options)); ``` ### Using `serveStatic` with Koa ```javascript app.use(require('lasso/middleware/koa').serveStatic(options)); ``` ## Runtime Optimization with Express and Koa
[__Sample App__](https://github.com/lasso-js-samples/lasso-express) To try out and experiment with the code, please see the following project:
[lasso-js-samples/lasso-express](https://github.com/lasso-js-samples/lasso-express)
Lasso.js has a smart caching layer and is fast enough so that it can be used at runtime as part of your server application. The easiest way to use Lasso.js at runtime is to use the Marko taglib and simply render the page template to the response output stream. The first time the page renders, the page will be lassoed and cached and the output of the lasso will be used to produce the final page HTML. After the first page rendering, the only work that will be done by Lasso.js is a simple cache lookup. By default, Lasso.js writes all resource bundles into the `static/` directory at the root of your application. In addition, by default, all resource URLs will be prefixed with `/static`. If resources are to be served up by the local Express server we will need to register the appropriate middleware as shown in the following sample code: __server.js__ ```javascript require('marko/node-require'); require('marko/express'); var express = require('express'); var compression = require('compression'); var serveStatic = require('serve-static'); // Load the page template: var template = require('./template.marko'); // Configure the default lasso require('lasso').configure({ }); var app = express(); // Enable gzip compression for all HTTP responses: app.use(compression()); // Any URL that begins with "/static" will be served up // out of the "static/" directory: app.use(require('lasso/middleware').serveStatic()); app.get('/', function(req, res) { // Render the page template as normal: res.marko(template, { name: 'Frank' }); }); ... app.listen(8080); ``` ## Bundling By default, all dependencies required for a page will be bundled into a single JavaScript bundle and a single CSS bundle. However, Lasso.js allows application-level bundles to be configured to allow for consistent bundles across pages and for multiple bundles to be included on a single page. Because Lasso.js also generates the HTML markup to include page bundles, the page itself does not need to be changed if the bundle configuration is changed. If a page has a dependency that is part of an application-level bundle then the dependency will be included as part of the application-level bundle instead of being aggregated with the page-level bundle. Bundles can be configured using the `"bundles"` configuration property that accepts an array of bundle configurations. Each bundle should consist of a name and a set of dependencies to assign to that bundle. __Bundling Example:__ Given the following configured bundles: ```json { ... "bundles": [ { "name": "bundle1", "dependencies": [ "./foo.js", "./baz.js" ] }, { "name": "bundle2", "dependencies": [ "./bar.js" ] } ] } ``` Optimizing a page that does not include any dependencies in application-level bundles: ```bash lasso app.js style.css --name my-page -c lasso-config.json ``` Output: ``` Output for page "my-page": Resource bundle files: static/my-page.js static/my-page.css HTML slots file: build/my-page.html.json ``` Optimizing a page that includes "foo.js" that is part of "bundle1": ```bash lasso app.js foo.js style.css --name my-page -c lasso-config.json ``` Output: ``` Output for page "my-page": Resource bundle files: static/my-page.js static/bundle1.js static/my-page.css HTML slots file: build/my-page.html.json ``` For more information on working with bundles. Please see the [bundling docs](docs/bundling.md). ## Code Splitting
[__Sample App__](https://github.com/lasso-js-samples/lasso-code-splitting) To try out and experiment with the code, please see the following project:
[lasso-js-samples/lasso-code-splitting](https://github.com/lasso-js-samples/lasso-code-splitting)
Lasso.js supports splitting out code that multiple pages/entry points have in common into separate bundles. This is accomplished by assigning an `intersection` dependency to a bundle. The `intersection` dependency is a package dependency that produces a set of dependencies that is the intersection of one or more packages. Code splitting ensures that the same code is not downloaded twice by the user when navigating a web application. The following bundle configuration illustrates how to split out common code into a separate bundle: ```json { "bundles": [ { "name": "common", "dependencies": [ { "intersection": [ "./src/pages/home/browser.json", "./src/pages/profile/browser.json" ] } ] } ] } ``` A less strict intersection condition is also supported via a `threshold` property. For example, to find those dependencies that are among *at least two* of the widgets: ```json { "bundles": [ { "name": "common", "dependencies": [ { "threshold": 2, "intersection": [ "require: ./a/widget", "require: ./b/widget", "require: ./c/widget" ] } ] } ] } ``` This could also be expressed as a percentage: ```json { "bundles": [ { "name": "common", "dependencies": [ { "threshold": "66%", "intersection": [ "require: ./a/widget", "require: ./b/widget", "require: ./c/widget" ] } ] } ] } ``` # Configuration ## Default Configuration ```javascript { // Write all bundles into the "static" directory "outputDir": "static", // URL prefix for all bundles "urlPrefix": "/static", // Include fingerprint in output files "fingerprintsEnabled": true } ``` ## Complete Configuration ```javascript { // Configure Lasso.js plugins "plugins": [ // Plugin with a default config: "lasso-less", // Plugin with custom configuration: { "plugin": "lasso-my-plugin", "config": { ... } }, ... ], // The base output directory for generated bundles "outputDir": "static", // Optional URL prefix to prepend to relative bundle paths "urlPrefix": "http://mycdn/static", // If fingerprints are enabled then a shasum will be included in the URL. // This feature is used for cache busting. "fingerprintsEnabled": true, // If fingerprints are not enabled then the same output file would be // used for bundles that go into the head and bundles that go in the // body. Enabling this option will ensure that bundles have unique names // even if fingerprints are disabled. "includeSlotNames": false // If "minify" is set to true then output CSS and JavaScript will run // through a minification transform. (defaults to false) "minify": false, "minifyJS": false, // Minify JavaScript "minifyCSS": false, // Minify CSS "minifyInlineOnly": false, // Only minify inline resources "minifyInlineJSOnly": false, // Only minify inline JavaScript resources "minifyInlineCSSOnly": false, // Only minify inline CSS resources // If "resolveCssUrls" is set to true then URLs found in CSS files will be // resolved and the original URLs will be replaced with the resolved URLs. // (defaults to true) "resolveCssUrls": true, // If "relativeUrlsEnabled" is set to false then URLs found in CSS files will // be absolute based on the urlPrefix. This default is false, which creates // relative URLs in CSS files. "relativeUrlsEnabled": true, // If "bundlingEnabled" is set to true then dependencies will be concatenated // together into one or more bundles. If set to false then each dependency // will be written to a separate file. (defaults to true) "bundlingEnabled": true, // If you want consistent bundles across pages then those shared bundles // can be specified below. If a page dependency is part of a shared // bundle then the shared bundle will be added to the page (instead of // adding the dependency to the page bundle). "bundles": [ { // Name of the bundle (used for determining the output filename) "name": "bundle1", // Set of dependencies to add to the bundle "dependencies": [ "./foo.js", "./baz.js" ] }, { "name": "bundle2", "dependencies": [ "./style/*.css", "require: **/*.js" ] } ], // The default name of the modules runtime variable is // ""$_mod" but you can change that with the noConflict option. // This is necessary if you have a webpage that loads // multiple JavaScript bundles that were // built at different times with Lasso. // The string you provide will be used to create // a unique name for the modules runtime variable name by // removing or replacing illegal characters. "noConflict": "myapp" } ``` # Node.js-style Module Support Lasso.js provides full support for transporting Node.js modules to the browser. If you write your modules in the standard Node.js way (i.e. using `require`, `module.exports` and `exports`) then the module will be able to be loaded on both the server and in the browser. This functionality is offered by the core [lasso-require](https://github.com/lasso-js/lasso-require) plugin which introduces a new `require` dependency type. For example: ```json [ "require: ./path-to-some-module" ] ``` If you want to include a module and have it run when loaded (i.e. self-executing) then you should use the `require-run` dependency type: ```json [ "require-run: ./main" ] ``` Examples of conditional requires: ```json [ { "require-run": "./foo", "if-flag": "bar" }, { "require": "./foo", "if-flag": "bar" } ] ``` It's also possible to remap a require based on a flag: ```json { "dependencies": [ ... ], "requireRemap": [ { "from": "./foo.js", "to": "./foo-mobile.js", "if-flag": "mobile" } ] } ``` The [lasso-require](https://github.com/lasso-js/lasso-require) plugin will automatically scan the source to find all `require(path)` calls to determine which additional modules need to be included in the output bundles (done recursively). For a `require` to automatically be detected it must be in the form `require("")` or `require.resolve("")`. The [lasso-require](https://github.com/lasso-js/lasso-require) plugin will automatically wrap all Node.js modules so that the psuedo globals (i.e. `require`, `module`, `exports`, `__filename` and `__dirname`) are made available to the module source code. The `lasso-require` plugin also supports [browserify shims](https://github.com/substack/node-browserify#compatibility) and [browserify transforms](https://github.com/substack/node-browserify/wiki/list-of-transforms). For more details on how the Node.js modules are supported on the browser, please see the documentation for the [lasso-js-samples/lasso-require](https://github.com/lasso-js/lasso-require) plugin. # Babel Support The [lasso-babel-transform](https://github.com/lasso-js/lasso-babel-transform) module provides support for transpiling JavaScript/JSX code using [babel](https://babeljs.io/). Please see the [lasso-babel-transform](https://github.com/lasso-js/lasso-babel-transform) docs for information on how to use that transform. # No Conflict Builds If you're using CommonJS modules in your project then this will cause the CommonJS runtime to be included in your build. The CommonJS runtime utilizes a global variable (`$_mod` by default). If your build output files need to co-exist with other JavaScript files that were built by Lasso separately then you need to make sure that your build produces a CommonJS runtime that is isolated from other builds. That is, you should not use the default `$_mod` global. To enable no-conflict build, you need to configure Lasso to use a unique CommonJS runtime global name. This can be done by setting the `noConflict` configuration property to string that is unique to your application or project. If you're using the JavaScript API then this is possible via: ```javascript // To configure the default Lasso for no-conflict builds: require('lasso').configure({ ... noConflict: 'myapp' }); // To create a new Lasso for no-conflict builds require('lasso').create({ ... noConflict: 'myapp' }); ``` See [Configuration](#configuration) for full list of configuration options. # Custom attributes for Script & Style tags It is also possible to add custom attributes to script and style tags for both inline and external resources. It is done using the attributes `inline-script-attrs`, `inline-style-attrs`, `external-style-attrs` and `external-script-attrs` as shown below. __page.marko__ ```marko ``` __browser.json__ ```json { "dependencies": [ { "path": "style-ext.css", "slot": "ext-css-slot" }, { "path": "test-ext.js", "slot": "ext-js-slot" }, "style.css", "test.js", { "path": "style-inline.css", "inline": true, "slot": "css-slot" }, { "path": "test-inline.js", "inline": true, "slot": "js-slot" } ] } ``` __Output HTML__ ```html ``` ## Use of defer/async with script tags If you add `async` or `defer` to a slot for external script attrs and Lasso encounters an inline script in that slot, it will wrap the code in a listener for `DOMContentLoaded` (for defer) or `load` (for async) to ensure that the script does not execute until the rest of the deferred scripts in that slot are loaded. __page.marko__ ```marko ``` __browser.json__ ```json { "dependencies": [ "test.js", { "path": "test-inline.js", "inline": true } ] } ``` __Output HTML__ ```html ``` # Content Security Policy Support Newer browsers support a web standard called Content Security Policy that prevents, among other things, cross-site scripting attacks by whitelisting inline ` ``` NOTE: A `nonce` attribute is only added to inline ` ``` The output HTML will be similar to the following: ```html ``` ## Securing Statically Built Pages If your page is statically built (such as when creating a Single Page App) then you should enable inline code fingerprinting which is way to whitelist exactly which inline code blocks should be allowed. It is important to emphasize, that a _nonce_ ("number once") will not properly secure a statically built application since the HTML is built once which prevents the nonce from changing. To secure your statically built application, you should instead fingerprint all of the inline code blocks and include these fingerprints in your CSP. Here is an example of what CSP might look like if using SHA256 fingerprints: `script-src 'self' 'sha256-viOn97JiWZ/fvh2VGIpROjZabjdtdrgtfO1wlPz9w7w='` ```javascript require('lasso').configure({ /* typical configuration goes here */ // Configure Lasso with a function that will be called for fingerprinting // each inline code block... fingerprintInlineCode: function(code) { var shasum = crypto.createHash('sha256'); shasum.update(code); return shasum.digest('base64'); } }); // This is the full list of fingerprints that were captured // across all page builds var inlineCodeFingerprints = []; // Collect all of the fingerprints as each page is built require('lasso').getDefaultLasso().on('afterLassoPage', function(event) { var lassoPageResult = event.result; var fingerprints = lassoPageResult.getInlineCodeFingerprints(); fingerprints.forEach(function(fingerprint) { inlineCodeFingerprints.push(fingerprint); }); }) // NOW BUILD YOUR PAGES HERE // ... build code goes here ... // NOW BUILD YOUR CONTENT SECURITY POLICY: var csp = inlineCodeFingerprints.map(function(fingerprint) { return `script-src 'self' 'sha256-${fingerprint}'` }).join('; '); ``` # Available Plugins Below is a list of plugins that are currently available: __Core plugins:__ * [lasso-require](https://github.com/lasso-js/lasso-require): Node.js-style require for the browser (similar to [browserify](https://github.com/substack/node-browserify)) * [lasso-minify-css](https://github.com/lasso-js/lasso-minify-css): Minify CSS files using [sqwish](https://github.com/ded/sqwish) * [lasso-minify-js](https://github.com/lasso-js/lasso-minify-js): Minify JavaScript files using [terser](https://github.com/terser-js/terser) * [lasso-resolve-css-urls](https://github.com/lasso-js/lasso-resolve-css-urls): Replace each resource URL in a CSS file with an lassoed resource URL __Third-party plugins__ * [lasso-dust](https://github.com/lasso-js/lasso-dust): Compile [Dust](https://github.com/linkedin/dustjs) template files to JavaScript * [lasso-handlebars](https://github.com/lasso-js/lasso-handlebars): Compile [Handlebars](http://handlebarsjs.com/) template files to JavaScript * [lasso-image](https://github.com/lasso-js/lasso-image): Get image info (including URL, width and height) for any image on both the server and client * [lasso-imagemin](https://github.com/lasso-js/lasso-imagemin): Minify GIF, PNG, JPG and SVG images during optimization * [lasso-jade](https://github.com/lasso-js/lasso-jade): Compile [Jade](http://jade-lang.com/) templates to JavaScript * [lasso-jsx](https://github.com/lasso-js/lasso-jsx): Compile [JSX](http://facebook.github.io/react/docs/jsx-in-depth.html) files to JavaScript * [lasso-less](https://github.com/lasso-js/lasso-less): Compile [Less](http://lesscss.org/) files to CSS * [lasso-lodash](https://github.com/lasso-js/lasso-lodash): Compile [Lo-Dash](https://lodash.com/) files to JavaScript * [lasso-marko](https://github.com/lasso-js/lasso-marko): Compile [Marko template](https://github.com/marko-js/marko) files to JavaScript * [lasso-sass](https://github.com/lasso-js/lasso-sass): Compile [Sass](https://github.com/sass/node-sass) files to CSS * [lasso-stylus](https://github.com/lasso-js/lasso-stylus): Compile [Stylus](http://learnboost.github.io/stylus/) files to CSS * [lasso-clean-css](https://github.com/yomed/lasso-clean-css): Minify CSS files using [clean-css](https://github.com/jakubpawlowicz/clean-css) * [lasso-autoprefixer](https://github.com/lasso-js/lasso-autoprefixer): Autoprefix CSS with vendor prefixes using [autoprefixer-core](https://github.com/postcss/autoprefixer-core) * [lasso-modernizr](https://github.com/darkwebdev/lasso-modernizr): Generate custom [Modernizr](https://modernizr.com) build * [lasso-optimize-iife](https://github.com/austinkelleher/lasso-optimize-iife): Optimize JavaScript immediately-invoked functions using [optimize-js](https://github.com/nolanlawson/optimize-js) * [lasso-rtl-css](https://github.com/shadiabuhilal/lasso-rtl-css): Transform CSS from left-to-right to right-to-left using [rtlcss](https://github.com/MohammadYounes/rtlcss) * [lasso-prepack](https://github.com/austinkelleher/lasso-prepack): Optimize JavaScript using [prepack](https://prepack.io/) * [lasso-typescript](https://github.com/ajay2507/lasso-typescript): compile [Typescript](https://www.typescriptlang.org/) in to Javascript. * [grunt-lasso](https://github.com/ajay2507/grunt-lasso): [Grunt](https://gruntjs.com/) plugin for Lasso js. * [lasso-analyzer](https://github.com/ajay2507/lasso-analyzer): Bundle Analyzer plugin for Lasso js. * [lasso-unpack](https://github.com/ajay2507/lasso-unpack): Generating an asset manifest for all source files . * [lasso-minify-transpile-inline](https://github.com/dsathyakumar/lasso-minify-transpile-inline): Lasso JS plugin to minify & transpile `inline` single file dependency assets that are not of `type: require`. * [rollup-plugin-lasso](https://github.com/dsathyakumar/rollup-plugin-lasso/): Bundles with Rollup (in cases where Lasso cannot be used) and pipes the output to Lasso - to be a part of Lasso's lifecycle. To use a third-party plugin, you must first install it using `npm install`. For example: ```bash npm install lasso-less --save ``` If you create your own plugin please send a Pull Request and it will show up above. Also, do not forget to tag your plugin with `lasso-plugin` and `lasso` in your `package.json` so that others can browse for it using [npm](https://www.npmjs.org/) # Extending Lasso.js Only read below if you are building plugins or transforms to further enhance the `lasso` module. ## Custom Plugins A plugin can be used to change how the lasso operates. This includes the following: * Register a custom dependency to support dependencies that compile to JS or CSS * Examples: * Register a dependency handler for "less" files to compiles Less to CSS * Register a dependency handler for "marko" files to compiles Marko template files to JS * Register a custom bundle writer * Examples: * Upload bundles to a resource server that backs a CDN instead of writing them to disk * Register output transforms * Examples: * Add an output transform to minify JavaScript code * Add an output transform to minify CSS code * Add an output transform to remove `console.log` from JS code * Add an output transform to resolve image URLs in CSS files * Configure the lasso * Examples: * Allow a plugin to automatically configure the lasso for production usage A plugin is simply a Node.js module that exports a function with the following signature: ```javascript /** * A plugin for Lasso.js * @param {lasso/lib/Lasso} lasso An instance of a Lasso that can be configured * @param {Object} The plugin configuration provided by the user */ module.exports = function(lasso, config) { // Register dependency types: lasso.dependencies.registerJavaScriptType('my-js-type', require('./dependency-my-js-type')); lasso.dependencies.registerStyleSheetType('my-css-type', require('./dependency-my-css-type')); lasso.dependencies.registerPackageType('my-package-type', require('./dependency-my-package-type')); // Add an output transform lasso.addTransform(require('./my-transform')); // Register a custom Node.js/CommonJS module compiler for a custom filename extension // var myModule = require('./hello.test'); lasso.dependencies.registerRequireExtension('test', function(path, context, callback) { callback(null, "exports.sayHello = function() { console.log('Hello!'); }"); }); }; ``` ## Custom Dependency Types There are three types of dependencies that are supported: * __JavaScript dependency:__ Produces JavaScript code * __CSS dependency:__ Produces CSS code * __Package dependency:__ Produces a package of additional JavaScript and CSS dependencies Each of these dependencies is described in the next few sections. However, it is recommended to also check out the source code of [available plugins](#available-plugins) listed above (e.g. [lasso-less](https://github.com/lasso-js/lasso-less)). ### Custom JavaScript Dependency Type If you would like to introduce your own custom dependency types then you will need to have your plugin register a dependency handler. This is illustrated in the following sample code: ```javascript const fs = require('fs'); module.exports = function myPlugin(lasso, config) { lasso.dependencies.registerJavaScriptType( 'my-custom-type', { // Declare which properties can be passed to the dependency type properties: { 'path': 'string' }, // Validation checks and initialization based on properties: async init (context) { if (!this.path) { throw new Error('"path" is required'); } // NOTE: resolvePath can be used to resolve a provided relative path to a full path this.path = this.resolvePath(this.path); }, // Read the resource: async read (context) { const src = await fs.promises.readFile(this.path, {encoding: 'utf8'}); return myCompiler.compile(src); // NOTE: A stream can also be returned }, // getSourceFile is optional and is only used to determine the last modified time // stamp and to give the output file a reasonable name when bundling is disabled getSourceFile: function() { return this.path; } }); }; ``` Once registered, the above dependency can then be referenced from an `browser.json` as shown in the following code: ```json { "dependencies": [ "my-custom-type: hello.file" ] } ``` If a custom dependency supports more than just a `path` property, additional properties could be provided as shown in the following sample code: ```json { "dependencies": [ { "type": "my-custom-type", "path": "hello.file", "foo": "bar", "hello": true } ] } ``` ### Custom CSS Dependency Type If you would like to introduce your own custom dependency types then you will need to have your plugin register a dependency handler as shown in the following sample code: ```javascript module.exports = function myPlugin(lasso, config) { lasso.dependencies.registerStyleSheetType( 'my-custom-type', handler); }; ``` The `handler` argument for a CSS dependency has the exact same interface as a handler for a JavaScript dependency (described earlier). ### Custom Package Type A custom package dependency can be used to dynamically resolve additional dependencies at optimization time. The sample package dependency handler below illustrates how a package dependency can be used to automatically include every file in a directory as a dependency: ```javascript const { promisify } = require('util'); const fs = promisify(require('fs')); const path = promisify(require('path')); lasso.dependencies.registerPackageType('dir', { properties: { 'path': 'string' }, async init (context) { let path = this.path; if (!path) { callback(new Error('"path" is required')); } this.path = path = this.resolvePath(path); // Convert the relative path to an absolute path const stat = await fs.stat(path); if (!stat.isDirectory()) { throw new Error('Directory expected: ' + path); } }, async getDependencies (context) { const dir = this.path; const filenames = await fs.readdir(dir); // Convert the filenames to full paths var dependencies = filenames.map(function(filename) { return path.join(dir, filename); }); return dependencies; }, getDir: function() { // If the dependencies are associated with a directory then return that directory. // Otherwise, return null return this.path; } }); ``` ## Custom Output Transforms Registered output transforms are used to process bundles as they are written to disk. As an example, an output transform can be used to minify a JavaScript or CSS bundle. Another example is that an output transform may be used to remove `console.log` statements from output JavaScript code. Transforms should be registered by a plugin using the `lasso.addTransform(transform)` method. As an example, the following unhelpful transform will convert all JavaScript source code to upper case: ```javascript module.exports = function (lasso, pluginConfig) { lasso.addTransform({ // Only apply to JavaScript code contentType: 'js', // 'css' is the other option // Give your module a friendly name (helpful for debugging in case something goes wrong in your code) name: module.id, // If stream is set to false then a String will be provided. Otherwise, a readable stream will be provided stream: false, // Do the magic: transform: function(code, lassoContext) { return code.toUpperCase(); } }); }; ``` Below is the streaming version of the same transform: ```javascript var through = require('through'); module.exports = function (lasso, pluginConfig) { lasso.addTransform({ // Only apply to JavaScript code contentType: 'js', // 'css' is the other option // Give your module a friendly name (helpful for debugging in case something goes wrong in your code) name: module.id, stream: true, // We want the code to be streamed to us // Do the magic: transform: function(inStream, lassoContext) { return inStream.pipe(through( function write(data) { this.queue(data.toUpperCase()); })); } }); }; ``` # JavaScript API See [JavaScript API](./docs/javascript-api.md). # AMD Compatibility See [AMD Compatibility](./docs/amd.md). # Sample Projects * [lasso-js-samples/lasso-async](https://github.com/lasso-js-samples/lasso-async): Demonstrates asynchronous/lazy dependency loading. * [lasso-js-samples/lasso-cli](https://github.com/lasso-js-samples/lasso-cli): Demonstrates the command-line interface. * [lasso-js-samples/lasso-code-splitting](https://github.com/lasso-js-samples/lasso-code-splitting): Demonstrates splitting out dependencies that are common across pages into a separate bundle. * [lasso-js-samples/lasso-config](https://github.com/lasso-js-samples/lasso-config): Demonstrates the usage of a JSON config file. * [lasso-js-samples/lasso-express](https://github.com/lasso-js-samples/lasso-express): Demonstrates using Lasso.js at runtime as part of an Express server app. * [lasso-js-samples/lasso-js-api](https://github.com/lasso-js-samples/lasso-js-api): Demonstrates how to use JavaScript API to lasso a page and inject the resulting head and body markup into a page. * [lasso-js-samples/lasso-taglib](https://github.com/lasso-js-samples/lasso-taglib): Demonstrates the use of the lasso taglib for Marko. * [lasso-js-samples/lasso-templates](https://github.com/lasso-js-samples/lasso-templates): Demonstrates the use of rendering the same templates on both the server and the client. # Discuss Please join us in the [Gitter chat room for Lasso.js](https://gitter.im/lasso-js/lasso) or [open a new Github issue](https://github.com/lasso-js/lasso/issues). # Maintainers * [Patrick Steele-Idem](https://github.com/patrick-steele-idem) (Twitter: [@psteeleidem](http://twitter.com/psteeleidem)) * [Phillip Gates-Idem](https://github.com/philidem/) (Twitter: [@philidem](https://twitter.com/philidem)) * [Michael Rawlings](https://github.com/mlrawlings) (Twitter: [@mlrawlings](https://twitter.com/mlrawlings)) # Contributors * Vinod Kumar (Twitter: [@vinodl](https://twitter.com/vinodl)) - [gulp-lasso](https://github.com/lasso-js/gulp-lasso) - [lasso-jsx](https://github.com/lasso-js/lasso-jsx) * Merwan Rodriguez (Twitter: [@uno7](https://twitter.com/uno7)) - [lasso-autoprefixer](https://github.com/lasso-js/lasso-autoprefixer) # Contribute Pull Requests welcome. Please submit Github issues for any feature enhancements, bugs or documentation problems. # License Apache License v2.0 ================================================ FILE: browser-refresh/index.js ================================================ module.exports = require('../src/browser-refresh'); ================================================ FILE: docs/amd.md ================================================ # AMD Compatibility Lasso.js does not support the AMD module syntax. You can, however, use the [deamdify](https://github.com/jaredhanson/deamdify) Browserify transform if you have third-party AMD code that should be transformed to CommonJS syntax. ## raptor-amd If you need a lightweight AMD runtime to support external code, you can also include the [raptor-amd](https://github.com/raptorjs/raptor-amd) module on your page. However, that module is no longer maintained and is only kept around for legacy reasons. ## Masking the AMD define function If you have both an AMD runtime and a CommonJS runtime on the same page then modules wrapped using a UMD wrapper that first checks for `define` (instead of `module.exports`) will attempt to define the module as an AMD module instead of using CommonJS. If you find that this issue is causing problems you can add a special `"mask-define": true` property as shown below: _browser.json_ ```json { "dependencies": [ { "path": "path/to/some-umd-module.js", "mask-define": true } ] } ``` This will result in code similar to the following: ```javascript (function(define) { /* mask define */ // typeof define === 'undefined' // ...third-party goes here }()); // END: mask define wrapper ``` ================================================ FILE: docs/bundling.md ================================================ Lasso.js Bundling ==================================== Bundling can either be enabled or disabled during page optimization. If bundling is disabled then every dependency will be written to its own file. If bundling is enabled then dependencies will be concatenated together based on the bundles configured for the application and page. Lasso.js allows both application-level and page-level bundles to be configured. # Application-level Bundles Application-level bundles are bundles that apply to every page that are lassoed. Application-level bundles allow for consistant bundles across pages when pages have common dependencies. Application-level bundles are typically configured as part of the lasso configuration as show below: ```json { ... "bundles": [ { "name": "jquery", "dependencies": [ "require: jquery" ] }, { "name": "math", "dependencies": [ "require: ./add" ] } ] } ``` While assigning dependencies to bundles during a page optimization, if the lasso detects that a dependency is part of a application-level bundle then that bundle will be added to the list of output bundles for the page and result in either a `', * 'head': '' * } * * * @return {Object} An object with slot names as property names and slot HTML as property values. */ get htmlBySlot() { const htmlBySlot = {}; for (const slotName in this._slotsByName) { if (hasOwn.call(this._slotsByName, slotName)) { const slotHtml = this.getHtmlForSlot(slotName); htmlBySlot[slotName] = slotHtml; } } return htmlBySlot; }, getHtmlBySlot: function() { return this.htmlBySlot; }, /** * Returns the HTML for a single slot. *

* Example out: * * "" * * * @param {String} slotName The name of the slot (e.g. "head" or "body") * @param {Object} data Input data to the slot that is used to render the actual slot HTML * @return {String} The HTML for the slot or an empty string if there is no HTML defined for the slot. */ getHtmlForSlot: function(slotName, data) { const slots = this._slotsByName[slotName]; if (slots) { const slotData = data || EMPTY_OBJECT; let html = ''; let sep = ''; for (const slot of slots) { html += sep + Slot.render(slot, slotData); sep = '\n'; } return html; } return ''; }, getHeadHtml: function(data) { return this.getHtmlForSlot('head', data); }, getBodyHtml: function(data) { return this.getHtmlForSlot('body', data); }, /** * Synonym for {@Link raptor/lasso/LassoPageResult#getHtmlForSlot} */ getSlotHtml: function(slotName, data) { return this.getHtmlForSlot(slotName, data); }, setSlotsByName: function(slotsByName) { this._slotsByName = slotsByName; }, registerBundle: function(bundle, async, lassoContext) { const bundleInfoMap = async ? this.infoByAsyncBundleName : this.infoByBundleName; const info = bundleInfoMap[bundle.name] || (bundleInfoMap[bundle.name] = {}); const url = bundle.getUrl(lassoContext); const slot = async ? undefined : bundle.slot; if (url) { this.addUrl(url, bundle.getSlot(), bundle.getContentType(), async, slot); info.url = url; } if (!bundle.isExternalResource && bundle.outputFile) { this.addFile(bundle.outputFile, bundle.getContentType(), async, slot); info.file = bundle.outputFile; } }, registerResource: function(resource) { this.resources.push(resource); }, addUrl: function(url, slot, contentType, isAsync) { if (!isAsync) { const urlsForSlot = this.urlsBySlot[slot] || (this.urlsBySlot[slot] = []); urlsForSlot.push(url); } const urlsForContentType = this.urlsByContentType[contentType] || (this.urlsByContentType[contentType] = []); urlsForContentType.push(url); }, getOutputFilesWithInfo() { return this.files; }, addFile: function(filePath, contentType, isAsync, slot) { this.files.push({ path: filePath, contentType, async: isAsync, slot }); }, /** * Returns the URLs of all the JavaScript resources for the page * @return {Array} An array of URLs */ getJavaScriptUrls: function() { return this.urlsByContentType.js || []; }, /** * Returns the URLs of all the CSS resources for the page * @return {Array} An array of URLs */ getCSSUrls: function() { return this.urlsByContentType.css || []; }, /** * Returns the URLs of all the JavaScript resources for the page * @return {Array} An array of URLs */ getUrlsForSlot: function(slot) { return this.urlsBySlot[slot] || []; }, /** * Returns the {@Link raptor/files/File} objects for all the JavaScript resources for the page * @return {Array} An array of File objects */ getJavaScriptFiles: function() { return this.getFilePathsByContentType('js'); }, getFilePathsByContentType(contentType) { const paths = []; this.files.forEach((file) => { if (file.contentType === contentType) { paths.push(file.path); } }); return paths; }, /** * Returns the {@Link raptor/files/File} objects for all the CSS resources for the page * @return {Array} An array of File objects */ getCSSFiles: function() { return this.getFilePathsByContentType('css'); }, getOutputFiles: function() { return this.getJavaScriptFiles().concat(this.getCSSFiles()); }, getFileByBundleName: function(bundleName) { const info = this.infoByBundleName[bundleName]; return info && info.file; }, getFileByAsyncBundleName: function(bundleName) { const info = this.infoByAsyncBundleName[bundleName]; return info && info.file; }, getUrlByBundleName: function(bundleName) { const info = this.infoByBundleName[bundleName]; return info && info.url; }, getUrlByAsyncBundleName: function(bundleName) { const info = this.infoByAsyncBundleName[bundleName]; return info && info.url; }, getInlineCodeFingerprints: function() { return this._inlineCodeFingerprints; }, setInlineCodeFingerprints: function(inlineCodeFingerprints) { this._inlineCodeFingerprints = inlineCodeFingerprints; }, toLassoPrebuild (name, flags) { return { slots: this._slotsByName, assets: this.resources, name, flags }; } }; module.exports = LassoPageResult; ================================================ FILE: src/LassoPrebuildResult.js ================================================ const fs = require('fs'); /** * We may want to store the prebuild in the same directory as the page * file instead of the current working directory. There will often be * an npm script that is running at the base of the project. In the * AWS Lambda case, we want the prebuild file to live alongside of the * page itself. We need to preprocess all of the builds and then write * them individually after because there may be multiple pages that live * in the same directory as well as multiple builds for the same page * with different flags. */ class LassoPrebuildResult { constructor () { this._buildsByPath = {}; } addBuild (path, build) { if (this._buildsByPath[path]) { this._buildsByPath[path].push(build); } else { this._buildsByPath[path] = [build]; } } async write () { for (const buildPath in this._buildsByPath) { await fs.promises.writeFile( buildPath, this.serializeBuild(buildPath), 'utf8'); } } serializeBuild (path) { const build = this._buildsByPath[path]; return JSON.stringify(build, null, 2); } getBuildByPath (path) { return this._buildsByPath[path]; } getBuildsByPath () { return this._buildsByPath; } } module.exports = LassoPrebuildResult; ================================================ FILE: src/LoaderMetadata.js ================================================ const AsyncPackage = require('./AsyncPackage'); function LoaderMetadata() { this._packageNames = Object.create(null); this._asyncPackagesByName = {}; } LoaderMetadata.prototype = { addAsyncPackageName: function(asyncName) { this._packageNames[asyncName] = true; }, addBundle: function(asyncName, bundle) { const asyncPackage = this._asyncPackagesByName[asyncName] || (this._asyncPackagesByName[asyncName] = new AsyncPackage(asyncName)); asyncPackage.addBundle(bundle); }, /** * This method is used by lasso-modules-client/transport/src/code-loader-metadata.js * to create simple object that can be stringified to generate code for * lasso metadata. */ toObject: function(context) { if (!context) { throw new Error('"context" is required'); } // EXAMPLE RESULT: /* { "foo": { js: ['a.js'], css: ['b.js'] } } */ const result = {}; for (const name in this._packageNames) { const asyncPackage = this._asyncPackagesByName[name]; if (asyncPackage) { result[name] = asyncPackage.getMeta(context); } else { result[name] = {}; } } return result; } }; module.exports = LoaderMetadata; ================================================ FILE: src/PageBundles.js ================================================ const forEachEntry = require('raptor-util').forEachEntry; /** * */ const PageBundles = function() { this.bundles = []; this.bundleLookup = {}; this.asyncBundleLookup = {}; }; PageBundles.prototype = { addSyncBundle: function(bundle) { /* * Add the bundle to a page slot if it has not already been added */ const bundleLookupKey = bundle.getKey(); if (!this.bundleLookup[bundleLookupKey]) { this.bundleLookup[bundleLookupKey] = bundle; this.bundles.push(bundle); } }, addAsyncBundle: function(bundle) { this.asyncBundleLookup[bundle.getKey()] = bundle; }, lookupSyncBundle: function(bundle) { return this.bundleLookup[bundle.getKey()]; }, forEachBundle: function(callback, thisObj) { this.bundles.forEach(callback, thisObj); }, forEachAsyncBundle: function(callback, thisObj) { forEachEntry(this.asyncBundleLookup, function(bundleKey, bundle) { callback.call(thisObj, bundle); }, this); }, forEachBundleIter: function() { return this.forEachBundle.bind(this); }, forEachAsyncBundleIter: function() { return this.forEachAsyncBundle.bind(this); } }; module.exports = PageBundles; ================================================ FILE: src/PageConfig.js ================================================ function PageConfig() { this.name = null; this.bundleSetConfig = null; this.packageManifest = null; } PageConfig.prototype = { getBundleSetConfig: function() { return this.bundleSetConfig; }, addBundleSetConfig: function(bundleSetConfig) { if (this.bundleSetConfig) { throw new Error('Page "' + this.name + '" already has bundles defined"'); } this.bundleSetConfig = bundleSetConfig; }, setPackageManifest: function(packageManifest) { this.packageManifest = packageManifest; }, getPackageManifest: function() { return this.packageManifest; }, getName: function() { return this.name; }, toString: function() { return '[PageConfig name=' + this.name + ']'; } }; module.exports = PageConfig; ================================================ FILE: src/Slot.js ================================================ const ok = require('assert').ok; const stringifyAttrs = require('./util/stringify-attrs'); function Slot(contentType) { ok(contentType, 'contentType is required'); this.contentType = contentType; this.content = []; } Slot.prototype = { addInlineCode: function(code, merge) { if (merge) { const prev = this.content.length ? this.content[this.content.length - 1] : null; if (prev && prev.inline && prev.merge && typeof prev.code === 'string') { prev.code += '\n' + code; return; } } this.content.push({ inline: true, code, merge: merge !== false }); }, addContent: function(content) { this.content.push({ inline: false, code: content }); } }; Slot.render = function(slot, data) { let html = ''; let sep = ''; for (const content of slot.content) { html += sep; sep = '\n'; switch (slot.contentType) { case 'js': html += (content.inline ? inlineScript : externalScript)(content.code, data); break; case 'css': html += (content.inline ? inlineStyle : externalStyle)(content.code, data); break; default: throw new Error('Invalid content type: ' + slot.contentType); } } return html; }; function inlineScript(content, data) { const scriptAttrs = data.externalScriptAttrs; let result = ``; if (scriptAttrs) { if (scriptAttrs.async) { result += wrapInDocumentLoaded(content, true); } else if (scriptAttrs.defer) { result += wrapInDocumentLoaded(content); } else { result += content; } } else { result += content; } return `${result}`; } function inlineStyle(content, data) { return `${content}`; } function externalScript(attrs, data) { return ``; } function externalStyle(attrs, data) { return ``; } function wrapInDocumentLoaded(code, isAsync) { return ( '(function() { var run = function() { ' + code + ' }; if (document.readyState ' + (isAsync ? '!== "complete"' : '=== "loading"') + ') { ' + (isAsync ? 'window.addEventListener("load"' : 'document.addEventListener("DOMContentLoaded"') + ', run); } else { run(); } })();' ); } module.exports = Slot; ================================================ FILE: src/SlotTracker.js ================================================ const Slot = require('./Slot'); const InlinePos = require('./InlinePos'); function SlotTracker() { this.slots = {}; this.slotNames = new Set(); } SlotTracker.prototype = { addInlineCode: function(slotName, contentType, code, inlinePos, mergeInline) { this.slotNames.add(slotName); let slotKey = contentType + ':' + slotName; if (inlinePos === InlinePos.BEGINNING) { slotKey += ':before'; } else if (inlinePos === InlinePos.END) { slotKey += ':after'; } const slot = this.slots[slotKey] || (this.slots[slotKey] = new Slot(contentType)); slot.addInlineCode(code, mergeInline); }, addContent: function(slotName, contentType, content) { this.slotNames.add(slotName); const slotKey = contentType + ':' + slotName; const slot = this.slots[slotKey] || (this.slots[slotKey] = new Slot(contentType)); slot.addContent(content); }, getSlotsByName: function() { const slotsByName = {}; const slots = this.slots; function addCode(slotList, key) { const slot = slots[key]; if (slot) slotList.push(slot); } for (const slotName of this.slotNames) { const slotList = slotsByName[slotName] = []; addCode(slotList, 'css:' + slotName + ':before'); addCode(slotList, 'css:' + slotName); addCode(slotList, 'css:' + slotName + ':after'); addCode(slotList, 'js:' + slotName + ':before'); addCode(slotList, 'js:' + slotName); addCode(slotList, 'js:' + slotName + ':after'); } return slotsByName; } }; module.exports = SlotTracker; ================================================ FILE: src/browser-refresh/index.js ================================================ const browserRefreshClient = require('browser-refresh-client'); const nodePath = require('path'); const styleExtensions = { css: true, less: true, styl: true, stylus: true, scss: true, sass: true, webp: true }; const imageExtensions = { png: true, jpeg: true, jpg: true, gif: true, svg: true }; let enabled = false; exports.enable = function(patterns) { if (!browserRefreshClient.isBrowserRefreshEnabled() || enabled) { return; } enabled = true; const lasso = require('../'); lasso.setDevelopmentMode(); if (!patterns) { // Reasonable default with client-side only files... patterns = '*.marko *.css *.less *.styl *.scss *.sass *.png *.jpeg *.jpg *.gif *.webp *.svg *.eot *.ttf *.woff *.woff2'; } browserRefreshClient .enableSpecialReload(patterns, { autoRefresh: false }) .onFileModified(function(path) { lasso.handleWatchedFileChanged(path); let extname = nodePath.extname(path); if (extname) { extname = extname.substring(1); } if (imageExtensions[extname]) { console.log('[lasso/browser-refresh] Image modified: ' + path); browserRefreshClient.refreshImages(); } else if (styleExtensions[extname]) { console.log('[lasso/browser-refresh] StyleSheet modified: ' + path); browserRefreshClient.refreshStyles(); } else { console.log('[lasso/browser-refresh] File modified: ' + path); browserRefreshClient.refreshPage(); } }); }; ================================================ FILE: src/bundle-builder.js ================================================ const dependencyWalker = require('./dependency-walker'); const DependencyTree = require('./DependencyTree'); const logger = require('raptor-logging').logger(module); const lassoPackageRoot = require('lasso-package-root'); const nodePath = require('path'); const recurseHandlers = { none: function(rootDependency, lassoContext) { return { shouldIncludeDependency: function(dependency) { return false; }, shouldRecurseIntoPackageDependency: function(dependency) { return false; } }; }, all: function(rootDependency, lassoContext) { return { shouldIncludeDependency: function(dependency) { return true; }, shouldRecurseIntoPackageDependency: function(dependency) { return true; } }; }, dir: function(rootDependency, lassoContext) { const baseDir = rootDependency.getDir(lassoContext) || ''; return { shouldIncludeDependency: function(dependency) { const dir = dependency.getDir(lassoContext); return dir === baseDir; }, shouldRecurseIntoPackageDependency: function(dependency) { if (dependency.getDir(lassoContext) === baseDir) { return true; } return false; } }; }, dirtree: function(rootDependency, lassoContext) { const baseDir = rootDependency.getDir(lassoContext); function checkDir(dependency) { if (!baseDir) { return false; } const dir = dependency.getDir(lassoContext); if (!dir) { return false; } return dir.startsWith(baseDir); } return { shouldIncludeDependency: function(dependency) { return checkDir(dependency); }, shouldRecurseIntoPackageDependency: function(dependency) { return checkDir(dependency); } }; }, module: function(rootDependency, lassoContext) { let baseDir = rootDependency.getDir(lassoContext); let nodeModulesDir; if (baseDir) { baseDir = lassoPackageRoot.getRootDir(baseDir); nodeModulesDir = nodePath.join(baseDir, 'node_modules'); } function checkDir(dependency) { if (!baseDir) { return false; } const dir = dependency.getDir(lassoContext); if (!dir) { return false; } return dir.startsWith(baseDir) && !dir.startsWith(nodeModulesDir); } return { shouldIncludeDependency: function(dependency) { return checkDir(dependency); }, shouldRecurseIntoPackageDependency: function(dependency) { return checkDir(dependency); } }; } }; function shouldIncludeDependency (lassoContext, rootDependency, recurseHandler, dependency) { if (dependency === rootDependency || (lassoContext.parentDependency && lassoContext.parentDependency === rootDependency)) { // Always include the root dependency or any child dependencies if the top-level // dependency was a package return true; } return recurseHandler.shouldIncludeDependency(dependency); } function shouldRecurseIntoPackageDependency (rootDependency, recurseHandler, dependency) { if (dependency === rootDependency) { // Always recurse into top-level package dependencies return true; } return recurseHandler.shouldRecurseIntoPackageDependency(dependency); } async function buildBundle(bundleMappings, dependencyRegistry, bundleConfig, lassoContext) { const tree = logger.isDebugEnabled() ? new DependencyTree() : null; const flags = lassoContext.flags; const bundleDependencies = bundleConfig.getDependencies(dependencyRegistry); const targetBundleName = bundleConfig.name; // Each bundle should have its own phase data. Phase data is just a bucket of data that can // be used by dependency handlers to keep track of what has been done for the current "phase" // of optimization. lassoContext.setPhase('app-bundle-mappings'); const dependencies = await bundleDependencies.normalize(); for (const rootDependency of dependencies) { await rootDependency.init(lassoContext); logger.debug('Root init'); let recurseInto = rootDependency._recurseInto || bundleConfig.getRecurseInto(); if (!recurseInto) { if (rootDependency.getDir()) { recurseInto = 'dirtree'; } else { recurseInto = 'all'; } } if (!recurseHandlers[recurseInto]) { throw new Error('Invalid recursion option: ' + recurseInto); } const recurseHandler = recurseHandlers[recurseInto](rootDependency, lassoContext); await dependencyWalker.walk({ lassoContext, dependency: rootDependency, flags, shouldSkipDependency (dependency) { if (dependency.isPageBundleOnlyDependency()) { return true; } if (bundleMappings.getBundleForDependency(dependency)) { // The dependency has already been added to another bundle return true; } if (dependency.isPackageDependency()) { return !shouldRecurseIntoPackageDependency(rootDependency, recurseHandler, dependency); } else if (!dependency.read) { // ignore non-readable dependencies during bundling phase return true; } return false; }, on: { dependency (dependency, context) { logger.debug(module.id, 'on dependency: ' + dependency.type); if (dependency.isPackageDependency()) { if (tree) { tree.add(dependency, context.parentDependency); } // We are only interested in non-package dependencies return; } if (shouldIncludeDependency(lassoContext, rootDependency, recurseHandler, dependency)) { bundleMappings.addDependencyToBundle( dependency, targetBundleName, context.slot, bundleConfig, lassoContext); if (tree) { tree.add(dependency, context.parentDependency); } } } } }); } if (tree) { logger.debug('Bundle "' + targetBundleName + '":\n' + tree.toString()); } } exports.buildBundle = buildBundle; ================================================ FILE: src/bundling-strategies.js ================================================ const logger = require('raptor-logging').logger(module); module.exports = { default: function(options, config, bundleMappings, pageBundles) { const pageName = options.name || options.pageName; const pageBundleName = pageName; const asyncPageBundleName = pageBundleName + '-async'; return { getBundleForSyncDependency: function(dependency, walkContext, debugTree) { const lassoContext = walkContext.lassoContext; const infoEnabled = logger.isInfoEnabled(); let bundleMapping = bundleMappings.getBundleMappingForDependency(dependency); if (bundleMapping && bundleMapping.bundle.isAsyncOnly()) { if (infoEnabled) { logger.info('Removing dependency ' + dependency + ' from bundle ' + bundleMapping.bundle); } // surgically remove the bundle from its existing mapping so that it can be // rebundled into a page bundle bundleMappings.removeBundleMapping(bundleMapping); bundleMapping = null; } let bundle; // Has this dependency been mapped into a bundle and is the bundle willing to accept // non-asynchronous dependencies? if (bundleMapping) { if (infoEnabled) { logger.info('Dependency ' + dependency + ' already mapped to bundle ' + bundleMapping.bundle); } // pull the bundle out of the bundle mapping bundle = bundleMapping.bundle; } else { if (infoEnabled) { logger.info('Dependency ' + dependency + ' is not mapped to a bundle'); } // either the dependency was not configured to fall into a bundle // or the bundle that it could have gone into is only for asynchronous // dependencies. Either way, we need to go ahead and add a new page // bundle for this dependency. bundle = bundleMappings.addDependencyToPageBundle( dependency, pageBundleName, walkContext.slot, null, lassoContext); } if (debugTree) { debugTree.addToBundle(bundle, dependency, walkContext.parentDependency); } return bundle; }, getBundleForAsyncDependency: function(dependency, walkContext, debugTree) { const lassoContext = walkContext.lassoContext; let bundle = bundleMappings.getBundleForDependency(dependency); if (bundle) { if (pageBundles.lookupSyncBundle(bundle)) { // This dependency has already been bundled with the page synchronously // so don't return a new async bundle. // Return null to indicate that this dependency does not belong // to an async bundle. return null; } } else { // Create a new bundle for the dependency if one does not exist bundle = bundleMappings.addDependencyToPageBundle( dependency, asyncPageBundleName, walkContext.slot, null, lassoContext); } if (debugTree) { debugTree.addToBundle(bundle, dependency, walkContext.parentDependency); } return bundle; } }; }, lean: function(options, config, bundleMappings, pageBundles) { const BundleMappings = require('./BundleMappings'); const pageName = options.name || options.pageName; const pageBundleName = pageName; const asyncPageBundleName = pageBundleName + '-async'; const pageBundleMappings = new BundleMappings(config, pageName); return { getBundleForSyncDependency: function(dependency, walkContext, debugTree) { const lassoContext = walkContext.lassoContext; // find the bundle that was configured for dependency const bundleMapping = bundleMappings.getBundleMappingForDependency(dependency); let bundle; if (bundleMapping && !bundleMapping.bundle.isAsyncOnly()) { bundle = pageBundleMappings.addDependencyToPageBundle( dependency, bundleMapping.bundle.getName(), walkContext.slot, bundleMapping.bundle.getConfig(), lassoContext); } else { bundle = pageBundleMappings.addDependencyToPageBundle( dependency, pageBundleName, walkContext.slot, null, lassoContext); } if (debugTree) { debugTree.addToBundle(bundle, dependency, walkContext.parentDependency); } return bundle; }, getBundleForAsyncDependency: function(dependency, walkContext, debugTree) { const lassoContext = walkContext.lassoContext; let bundle = pageBundleMappings.getBundleForDependency(dependency); if (bundle) { if (pageBundles.lookupSyncBundle(bundle)) { // This dependency has already been bundled with the page synchronously // so don't add it to an async bundle. // Return null to indicate that this dependency does not belong // to an async bundle. return null; } } else { // this dependency has not been bundled yet const bundleMapping = bundleMappings.getBundleMappingForDependency(dependency); if (bundleMapping) { // this dependency has been mapped into a bundle const bundleName = bundleMapping.bundle.isAsyncOnly() ? bundleMapping.bundle.getName() : bundleMapping.bundle.getName() + '-async'; bundle = pageBundleMappings.addDependencyToPageBundle( dependency, bundleName, walkContext.slot, bundleMapping.bundle.getConfig(), lassoContext); } else { bundle = pageBundleMappings.addDependencyToPageBundle( dependency, asyncPageBundleName, walkContext.slot, null, lassoContext); } if (debugTree) { debugTree.addToBundle(bundle, dependency, walkContext.parentDependency); } } return bundle; } }; } }; ================================================ FILE: src/caching-fs.js ================================================ module.exports = require('lasso-caching-fs'); ================================================ FILE: src/condition.js ================================================ exports.ifCondition = function(condition) { // This is a hack because we moved from "flags" to "extensions" // We create a function that introduces two scoped variables: flags, extensions const conditionFunc = eval('(function(flags, extensions) { return ' + condition + ';})'); return function(flags) { return conditionFunc(flags, flags); }; }; exports.ifFlag = function(flag) { return function(flags) { return flags.contains(flag); }; }; exports.ifNotFlag = function(flag) { return function(flags) { return !flags.contains(flag); }; }; exports.fromObject = function(o) { let condition; if ((condition = o.if)) { return exports.ifCondition(condition); } else if ((condition = o['if-flag'] || o['if-extension'])) { return exports.ifFlag(condition); } else if ((condition = o['if-not-flag'] || o['if-not-extension'])) { return exports.ifNotFlag(condition); } return null; }; ================================================ FILE: src/config-loader.js ================================================ const Config = require('./Config'); const BundleSetConfig = require('./BundleSetConfig'); const BundleConfig = require('./BundleConfig'); const nodePath = require('path'); const fileWriterFactory = require('./writers/file-writer'); const fs = require('fs'); const ok = require('assert').ok; const propertyHandlers = require('property-handlers'); const minifyJSPlugin = require('./plugins/lasso-minify-js'); const minifyCSSPlugin = require('./plugins/lasso-minify-css'); const resolveCssUrlsPlugin = require('./plugins/lasso-resolve-css-urls'); const extend = require('raptor-util/extend'); const resolveFrom = require('resolve-from'); const complain = require('complain'); const hasOwn = Object.prototype.hasOwnProperty; function findRootDir(dirname) { if (dirname === '' || dirname === '/') { return null; } const packagePath = nodePath.join(dirname, 'package.json'); if (dirname.indexOf('node_modules') === -1 && fs.existsSync(packagePath)) { return dirname; } const parentDirname = nodePath.dirname(dirname); if (parentDirname !== dirname) { return findRootDir(parentDirname); } else { return null; } } function load(options, baseDir, filename, configDefaults) { /* jshint loopfunc:true */ ok(baseDir, '"baseDir" argument is required'); function getProjectRootDir(dirname) { let rootDir = findRootDir(dirname); if (!rootDir) { rootDir = baseDir; } return rootDir; } if (!options || typeof options !== 'object') { throw new Error('Invalid options. Object expected'); } if (options.fileWriter) { // Move fileWriter properties to the root extend(options, options.fileWriter); delete options.fileWriter; } if (configDefaults) { for (const key in configDefaults) { // copy defaults to options only if options doesn't already // have that property if (hasOwn.call(configDefaults, key) && !hasOwn.call(options, key)) { options[key] = configDefaults[key]; } } } const config = new Config(); config.rawConfig = options; const fileWriterConfig = {}; function addBundles(bundleSetName, bundles) { const bundleSetConfig = new BundleSetConfig(bundleSetName); bundles.forEach(function(bundle) { const bundleConfig = new BundleConfig(baseDir, filename); bundleConfig.name = bundle.name; bundleConfig.asyncOnly = bundle.asyncOnly; if (bundle.recursive !== undefined) { bundleConfig.recurseInto = (bundle.recursive === true) ? 'all' : 'none'; } if (bundle.dependencies) { bundle.dependencies.forEach(function(d) { // "recursive" is not an allowed dependency // property but we need this property to determine // how to build the bundle. Prefixing with an // underscore allows the property to go through if (d.recursive === true) { d.recurseInto = 'all'; } else if (d.recursive === false) { d.recurseInto = 'none'; } d._recurseInto = d.recurseInto; delete d.recursive; delete d.recurseInto; }); } bundleConfig.addDependencies(bundle.dependencies); bundleSetConfig.addBundleConfig(bundleConfig); }); config.addBundleSetConfig(bundleSetConfig); } const handlers = { require: function(value) { if (value) { extend(config._requirePluginConfig, value); } }, minify: function(value) { if (value) { config.addPlugin(minifyJSPlugin, config._minifyJSPluginConfig); config.addPlugin(minifyCSSPlugin, config._minifyCSSPluginConfig); } }, minifyJS: function(value) { if (value) { config.addPlugin(minifyJSPlugin, config._minifyJSPluginConfig); } }, minifyCSS: function(value) { if (value) { config.addPlugin(minifyCSSPlugin, config._minifyCSSPluginConfig); } }, minifyInlineOnly: function(value) { if (value) { config.addPlugin(minifyJSPlugin, config._minifyJSPluginConfig); config.addPlugin(minifyCSSPlugin, config._minifyCSSPluginConfig); config._minifyJSPluginConfig.inlineOnly = true; config._minifyCSSPluginConfig.inlineOnly = true; } }, minifyInlineJSOnly: function(value) { if (value) { config.addPlugin(minifyJSPlugin, config._minifyJSPluginConfig); config._minifyJSPluginConfig.inlineOnly = true; } }, minifyInlineCSSOnly: function(value) { if (value) { config.addPlugin(minifyCSSPlugin, config._minifyCSSPluginConfig); config._minifyCSSPluginConfig.inlineOnly = true; } }, resolveCssUrls: function(value) { if (value) { // the value can be a plugin config or a truthy value if (value === true) { // value is a boolean so no config was provided so use an empty object value = {}; } config.addPlugin(resolveCssUrlsPlugin, value); } }, relativeUrlsEnabled: function(value) { fileWriterConfig.relativeUrlsEnabled = value === true; }, bundlingEnabled: function(value) { config.setBundlingEnabled(value === true); }, bundlingStrategy: function(value) { config.setBundlingStrategy(value); }, fingerprintsEnabled: function(value) { fileWriterConfig.fingerprintsEnabled = value === true; }, outputDir: function(value) { value = nodePath.resolve(baseDir, value); fileWriterConfig.outputDir = value; }, urlPrefix: function(value) { fileWriterConfig.urlPrefix = value; }, includeSlotNames: function(value) { fileWriterConfig.includeSlotNames = value === true; }, fingerprintLength: function(value) { fileWriterConfig.fingerprintLength = value; }, flags: function(flags) { config.setFlags(flags); }, /** * @deprecated */ extensions (flags) { complain('"configLoader.extensions(...)" is deprecated. Please use "configLoader.flags(...)" instead.'); config.setFlags(flags); }, /** * @deprecated */ enabledExtensions (flags) { complain('"configLoader.enabledExtensions(...)" is deprecated. Please use "configLoader.flags(...)" instead.'); config.setFlags(flags); }, bundles: function(bundles) { if (bundles) { addBundles('default', bundles); } }, inPlaceDeploymentEnabled: function(value) { config.setInPlaceDeploymentEnabled(value === true); }, inPlaceDeployment: function(inPlaceDeploymentOptions) { if (typeof inPlaceDeploymentOptions === 'boolean') { config.setInPlaceDeploymentEnabled(inPlaceDeploymentOptions === true); } else { propertyHandlers(inPlaceDeploymentOptions, { enabled: function(value) { config.setInPlaceDeploymentEnabled(value === true); }, urlPrefix: function(value) { config.setInPlaceUrlPrefix(value); } }, 'config.inPlaceDeployment'); } }, plugins: function(value) { if (value != null) { if (!Array.isArray(value)) { value = Object.keys(value).map(function(moduleName) { const pluginConfig = value[moduleName]; return { plugin: moduleName, config: pluginConfig }; }); } for (let i = 0; i < value.length; i++) { let pluginInfo = value[i]; if (typeof pluginInfo === 'string' || typeof pluginInfo === 'function') { pluginInfo = { plugin: pluginInfo, config: {} }; } if (pluginInfo.plugin === 'lasso-minify-js') { extend(config._minifyJSPluginConfig, pluginInfo.config); continue; } else if (pluginInfo.plugin === 'lasso-minify-css') { extend(config._minifyCSSPluginConfig, pluginInfo.config); continue; } let pluginFunc = null; let pluginConfig = null; let enabled = true; propertyHandlers(pluginInfo, { plugin: function(plugin) { if (typeof plugin === 'string') { let resolvedPath = null; try { resolvedPath = resolveFrom(baseDir, plugin); } catch (e2) { throw new Error('Plugin module not found for "' + plugin + '". Searched from "' + baseDir + '"'); } pluginFunc = require(resolvedPath); } else { pluginFunc = plugin; } }, config: function(value) { pluginConfig = value; }, enabled: function(value) { enabled = value; } }, 'config.plugins'); pluginConfig = pluginConfig || {}; if (enabled === false || pluginConfig.enabled === false) { continue; } config.addPlugin(pluginFunc, pluginConfig); } } }, projectRoot: function(value) { config.setProjectRoot(nodePath.resolve(baseDir, value)); }, cacheProfile: function(value) { config.setCacheProfile(value); }, cacheDir: function(value) { config.setCacheDir(value); }, cacheProfiles: function(value) { config.setCacheProfiles(value); }, bundleReadTimeout: function(value) { config.setBundleReadTimeout(value); }, /** * Whether Lasso should load from a prebuild configuration or not */ loadPrebuild (value) { config.setLoadPrebuild(value); }, resolver (value) { config.setResolver(value); }, noConflict: function(value) { if (value) { if (value.constructor !== String) { throw new Error('Value for "noConflict" should be a string that uniquely identifies your project'); } // Build the variable name for the modules global // (the lasso-require plugin will sanitize it by // removing/replacing any illegal characters) config.modulesRuntimeGlobal = config._requirePluginConfig.modulesRuntimeGlobal = '$_mod_' + value; // The "unbundledTargetPrefix" is used to create sub-directory // in the output folder for some of the lasso-require // dependencies. config._requirePluginConfig.unbundledTargetPrefix = value; } }, cspNonceProvider: function(value) { if (typeof value !== 'function') { throw new Error('"cspNonceProvider" should be a function'); } config.setCSPNonceProvider(value); }, fingerprintInlineCode: function(value) { if (typeof value !== 'function') { throw new Error('"fingerprintInlineCode" should be a function'); } config.setFingerprintInlineCode(value); }, cacheKey (value) { config.setCacheKey(value); } }; propertyHandlers(options, handlers, 'config'); config.fileWriterConfig = fileWriterConfig; config.writer = fileWriterFactory(fileWriterConfig, config); if (!config.getProjectRoot()) { config.setProjectRoot(getProjectRootDir(baseDir)); } return config; } exports.load = load; ================================================ FILE: src/content-types.js ================================================ exports.CSS = 'css'; exports.JS = 'js'; exports.NONE = 'none'; ================================================ FILE: src/dependencies/Dependency.js ================================================ const nodePath = require('path'); const condition = require('../condition'); const CONTENT_TYPE_CSS = require('../content-types').CSS; const CONTENT_TYPE_JS = require('../content-types').JS; const CONTENT_TYPE_NONE = require('../content-types').NONE; const util = require('../util'); const ok = require('assert').ok; const equal = require('assert').equal; const Readable = require('stream').Readable; const manifestLoader = require('../manifest-loader'); const logger = require('raptor-logging').logger(module); const lastModified = require('../last-modified'); const AsyncValue = require('raptor-async/AsyncValue'); const EventEmitter = require('events').EventEmitter; const hasOwn = Object.prototype.hasOwnProperty; const NON_KEY_PROPERTIES = { inline: true, slot: true, 'js-slot': true, 'css-slot': true, getDefaultBundleName: true }; function getPackagePath(d) { return d.__filename ? d.__filename : '(unknown)'; } function doCalculateFingerprint (dependency, lassoContext) { return new Promise((resolve, reject) => { const input = dependency.read(lassoContext); const cachingReadStream = util.createCachingStream(); const fingerprintStream = util.createFingerprintStream() .on('error', reject) .on('fingerprint', function(fingerprint) { dependency._cachingReadStream = cachingReadStream; if (logger.isDebugEnabled()) { logger.debug('Dependency ' + dependency.toString() + ' fingerprint key: ' + fingerprint); } resolve(fingerprint); }); // Don't buffer the data so that the stream can be drained fingerprintStream.resume(); input .on('error', function(e) { let message = 'Unable to read dependency "' + dependency + '" referenced in "' + dependency.getParentManifestPath() + '". '; if (e.code === 'ENOENT' && e.path) { message += 'File does not exist: ' + e.path; } else { message += 'Error: ' + (e.stack || e); } e = new Error(message); e.dependency = dependency; reject(e); }) .pipe(cachingReadStream) .pipe(fingerprintStream); }); }; // This is a simple stream implementation that either has code available // immediately or it is waiting for code to be made available upon // callback completion function DependencyReadable() { } require('util').inherits(DependencyReadable, Readable); DependencyReadable.prototype._read = function() { // don't need to actually implement _read because }; function Dependency(dependencyConfig, dirname, filename) { ok(dependencyConfig != null, '"dependencyConfig" is a required argument'); equal(typeof dependencyConfig, 'object', '"dependencyConfig" should be an object'); ok(dirname, '"dirname" is a required argument'); equal(typeof dirname, 'string', '"dirname" should be a string'); this._resolvedInit = undefined; this._keyAsyncValue = undefined; this._lastModifiedAsyncValue = undefined; this._cachingReadStream = undefined; // The directory associated with this dependency. // If the dependency was defined in an browser.json // file then the directory is the directory in which // browser.json is found. this.__dirname = dirname; this.__filename = filename; this.type = dependencyConfig.type; this._condition = condition.fromObject(dependencyConfig); this._events = undefined; this.set(dependencyConfig); } Dependency.prototype = { __Dependency: true, properties: { type: 'string', attributes: 'object', inline: 'string', slot: 'string', 'css-slot': 'string', 'js-slot': 'string', // TODO: Change: Should these be removed? if: 'string', 'if-extension': 'string', /* DEPRECRATED */ 'if-not-extension': 'string', /* DEPRECRATED */ 'if-flag': 'string', 'if-not-flag': 'string', getDefaultBundleName: 'function' }, async init (lassoContext) { this._context = lassoContext; if (this._resolvedInit) { return; } await this.doInit(lassoContext); this._resolvedInit = true; }, async doInit (lassoContext) {}, createReadStream: function(lassoContext) { if (this._cachingReadStream) { return this._cachingReadStream.createReplayStream(); } return this.doRead(lassoContext); }, /** * * @deprecated use createReadStream instead */ read: function(lassoContext) { return this.createReadStream(lassoContext); }, set: function(props) { const propertyTypes = this.properties; for (const k in props) { if (hasOwn.call(props, k)) { let v = props[k]; if (propertyTypes) { const type = propertyTypes[k]; if (!type && !k.startsWith('_')) { throw new Error('Dependency of type "' + this.type + '" does not support property "' + k + '". Package: ' + getPackagePath(this)); } if (type && typeof v === 'string') { if (type === 'boolean') { v = (v === 'true'); } else if (type === 'int' || type === 'integer') { v = parseInt(v, 10); } else if (type === 'float' || type === 'number') { v = parseFloat(v); } else if (type === 'path') { v = this.resolvePath(v); } } } this[k] = v; } } }, /* * This resolve method will use the module search path to resolve * the given path if the path does not begin with "." or "/". * * For example, dependency.resolvePath('some-module/a.js') * will use the NodeJS module search path starting from this dependencies directory. * If resolution fails using the NodeJS module search path, then an attempt * will be made to resolve the path as a relative path. * * dependency.resolvePath('./a.js') we be resolved relative to the * directory of this dependency. * * dependency.resolvePath('/a.js') is assumed to be absolute * (possibly because it was already resolved). */ resolvePath: function(path, from) { const result = this._context.resolve(path, from || this.__dirname, { moduleFallbackToRelative: true }); return result && result.path; }, getParentManifestDir: function() { return this.__dirname; }, getParentManifestPath: function() { return this.__filename; }, isPackageDependency: function() { return this._packageDependency === true; }, /** * This method is called to determine if a depednency can be added to a shared * application bundle or if it can only be added to a page bundle. * This method can be overridden, but the default behavior is to return * false to indicate that it can be added to either a shared application * bundle or a page-specific bundle. * @return {boolean} Returns true if this is a page-bundle only dependency. False, otherwise.s */ isPageBundleOnlyDependency: function() { return false; }, onAddToPageBundle: function(bundle, lassoContext) { // subclasses can override }, onAddToAsyncPageBundle: function(bundle, lassoContext) { // subclasses can override }, async getPackageManifest (lassoContext) { if (!this.isPackageDependency()) { throw new Error('getPackageManifest() failed. Dependency is not a package: ' + this.toString()); } let manifest; if (typeof this.loadPackageManifest === 'function') { const packageManifestResult = await this.loadPackageManifest(lassoContext); if (!packageManifestResult) { return null; } const dependencyRegistry = this.__dependencyRegistry; const LassoManifest = require('../LassoManifest'); if (typeof packageManifestResult === 'string') { const manifestPath = packageManifestResult; const from = this.getParentManifestDir(); try { manifest = this.createPackageManifest(manifestLoader.load(manifestPath, from)); } catch (e) { let err; if (e.fileNotFound) { err = new Error('Lasso manifest not found for path "' + manifestPath + '" (searching from "' + from + '"). Dependency: ' + this.toString()); } else { err = new Error('Unable to load lasso manifest for path "' + manifestPath + '". Dependency: ' + this.toString() + '. Exception: ' + (e.stack || e)); } throw err; } } else if (!LassoManifest.isLassoManifest(packageManifestResult)) { manifest = new LassoManifest({ manifest: packageManifestResult, dependencyRegistry, dirname: this.getParentManifestDir(), filename: this.getParentManifestPath() }); } else { manifest = packageManifestResult; } return manifest; } else if (typeof this.getDependencies === 'function') { const dependencies = await this.getDependencies(lassoContext); if (dependencies) { manifest = this.createPackageManifest(dependencies); } return manifest; } else { throw new Error('getPackageManifest() failed. "getDependencies" or "loadPackageManifest" expected: ' + this.toString()); } }, _getKeyPropertyNames: function() { return Object.keys(this) .filter(function(k) { return !k.startsWith('_') && k !== 'type' && !hasOwn.call(NON_KEY_PROPERTIES, k); }, this) .sort(); }, getKey: function() { if (!this._keyAsyncValue || !this._keyAsyncValue.isResolved()) { return null; // throw new Error('getKey() was called before key was calculated'); } return this._keyAsyncValue.data; }, /** * getReadCacheKey() must be a unique key across all lasso context since * it is flattened to single level and shared by multiple lasso instances. */ getReadCacheKey: function() { return this.getPropsKey(); }, getPropsKey: function() { return this._propsKey || (this._propsKey = this.calculateKeyFromProps()); }, /** * calculateKey() is used to calculate a unique key for this dependency * that is unique within the given lasso context. * * getReadCacheKey() must be a unique key across all lasso context since * it is flattened to single level and shared by multiple lasso instances. */ calculateKey (lassoContext) { // TODO: Change to fully use async/await return new Promise((resolve, reject) => { function callback (err, res) { return err ? reject(err) : resolve(res); } if (this._key !== undefined) { return callback(null, this._key); } if (this._keyAsyncValue) { // Attach a listener to the current in-progres check return this._keyAsyncValue.done(callback); } // no data holder so let's create one let keyAsyncValue; this._keyAsyncValue = keyAsyncValue = new AsyncValue(); this._keyAsyncValue.done(callback); const handleKey = (key) => { if (key === null) { key = this.type + '|' + lassoContext.uniqueId(); } else if (typeof key !== 'string') { keyAsyncValue.reject(new Error('Invalid key: ' + key)); return; } if (logger.isDebugEnabled()) { logger.debug('Calculated key for ' + this.toString() + ': ' + key); } // Store resolve key in "_key" for quick lookup this._key = key; keyAsyncValue.resolve(key); }; const keyResult = this.doCalculateKey(lassoContext); if ((typeof keyResult === 'string') && !keyAsyncValue.isSettled()) { handleKey(keyResult); } else if (keyResult && typeof keyResult === 'object' && keyResult.then) { keyResult .then((key) => { handleKey(key); }) .catch((err) => { keyAsyncValue.reject(err); }); } else { return handleKey(keyResult); } }); }, doCalculateKey (lassoContext) { if (this.isPackageDependency()) { return this.calculateKeyFromProps(); } else if (this.isExternalResource()) { const url = this.getUrl ? this.getUrl(lassoContext) : this.url; return url; } else { if (lassoContext.cache) { return this.getLastModified(lassoContext) .then((lastModified) => { if (!lastModified) { return doCalculateFingerprint(this, lassoContext); } return lassoContext.cache.getDependencyFingerprint( // cache key this.getPropsKey(), // last modified timestamp (if cache entry is older than this then builder will be called) lastModified, // builder doCalculateFingerprint.bind(null, this, lassoContext)); }); } else { return doCalculateFingerprint(this, lassoContext); } } }, calculateKeyFromProps: function() { const key = this._getKeyPropertyNames() .map(function(k) { return k + '=' + this[k]; }, this) .join('|'); return this.type + '|' + key; }, hasContent: function() { return this.contentType !== CONTENT_TYPE_NONE; }, isJavaScript: function() { return this.contentType === CONTENT_TYPE_JS; }, isStyleSheet: function() { return this.contentType === CONTENT_TYPE_CSS; }, getSlot: function() { if (this.slot) { return this.slot; } if (this.isStyleSheet()) { return this.getStyleSheetSlot(); } else { return this.getJavaScriptSlot(); } }, hasModifiedFingerprint: function() { return false; }, getContentType: function() { return this.contentType; }, isCompiled: function() { return false; }, isInPlaceDeploymentAllowed: function() { return this.type === 'js' || this.type === 'css'; }, isExternalResource: function() { return false; }, getJavaScriptSlot: function() { return this['js-slot'] || this.slot; }, getStyleSheetSlot: function() { return this['css-slot'] || this.slot; }, async getLastModified (lassoContext) { if (this._lastModifiedValue) { return this._lastModifiedValue; } const getLastModified = (lastModified) => lastModified == null || lastModified < 0 ? 0 : lastModified; const lastModified = this._lastModifiedValue = getLastModified(await this.doGetLastModified(lassoContext)); return lastModified; }, async doGetLastModified (lassoContext, callback) { const sourceFile = this.getSourceFile(); if (sourceFile) { return this.getFileLastModified(sourceFile); } else { return 0; } }, async getFileLastModified (path) { return lastModified.forFile(path); }, createPackageManifest: function(manifest, dirname, filename) { const LassoManifest = require('../LassoManifest'); if (Array.isArray(manifest) || !manifest) { manifest = { dependencies: manifest || [] }; } else { dirname = manifest.dirname; filename = manifest.filename; } return new LassoManifest({ manifest, dependencyRegistry: this.__dependencyRegistry, dirname: dirname || this.getParentManifestDir(), filename: filename || this.getParentManifestPath() }); }, getDir: function() { const sourceFile = this.getSourceFile(); // if (!sourceFile) { // throw new Error('Unable to determine directory that dependency is associated with because getSourceFile() returned null and getDir() is not implemented. Dependency: ' + this.toString()); // } return (sourceFile) ? nodePath.dirname(sourceFile) : null; }, getSourceFile: function() { return null; }, toString() { const entries = []; const type = this.type; for (const k in this) { if (hasOwn.call(this, k) && !k.startsWith('_') && k !== 'type') { const v = this[k]; entries.push(k + '=' + JSON.stringify(v)); } } // var packagePath = this.getParentManifestPath(); // if (packagePath) { // entries.push('packageFile="' + packagePath + '"'); // } return '[' + type + (entries.length ? (': ' + entries.join(', ')) : '') + ']'; }, shouldCache: function(lassoContext) { let cacheable = true; let isStatic = false; const cacheConfig = this.cacheConfig; if (cacheConfig) { cacheable = cacheConfig.cacheable !== false; isStatic = cacheConfig.static === true; } else { cacheable = this.cache !== false; } if (isStatic) { const transformer = lassoContext.transformer; if (!transformer || transformer.hasTransforms() === false) { // Don't bother caching a dependency if it is static and there are no transforms return false; } } return cacheable; }, emit: function() { if (!this._events) { // No listeners return; } return this._events.emit.apply(this._events, arguments); }, on: function(event, listener) { if (!this._events) { this._events = new EventEmitter(); } return this._events.on(event, listener); }, once: function(event, listener) { if (!this._events) { this._events = new EventEmitter(); } return this._events.once(event, listener); }, removeListener: function(event, listener) { if (!this._events) { // Nothing to remove return; } return this._events.removeListener.apply(this._events, arguments); }, removeAllListeners: function(event) { if (!this._events) { // Nothing to remove return; } return this._events.removeAllListeners.apply(this._events, arguments); }, getDefaultBundleName: function(pageBundleName, lassoContext) { return this.defaultBundleName; }, inspect: function() { const inspected = { type: this.type }; this._getKeyPropertyNames() .forEach((k) => { inspected[k] = this[k]; }); return inspected; } }; Dependency.prototype.addListener = Dependency.prototype.on; module.exports = Dependency; ================================================ FILE: src/dependencies/DependencyRegistry.js ================================================ const nodePath = require('path'); const extend = require('raptor-util').extend; const inherit = require('raptor-util').inherit; const Dependency = require('./Dependency'); const CONTENT_TYPE_CSS = require('../content-types').CSS; const CONTENT_TYPE_JS = require('../content-types').JS; const ok = require('assert').ok; const typePathRegExp = /^([A-Za-z0-9_\-]{2,})\s*:\s*(.+)$/; // Hack: {2,} is used because Windows file system paths start with "c:\" const readStream = require('../util').readStream; const RequireHandler = require('./RequireHandler'); const equal = require('assert').equal; const globNormalizer = require('./glob').normalizer; const dependencyResource = require('./dependency-resource'); const logger = require('raptor-logging').logger(module); const slice = Array.prototype.slice; const hasOwn = Object.prototype.hasOwnProperty; function createDefaultNormalizer(registry) { function parsePath(path) { const typePathMatches = typePathRegExp.exec(path); if (typePathMatches) { return { type: typePathMatches[1], path: typePathMatches[2] }; } else { let type = registry.typeForPath(path); if (!type) { type = 'package'; } return { type, path }; } } return function(dependency) { if (typeof dependency === 'string') { dependency = parsePath(dependency); } else if (!Array.isArray(dependency)) { dependency = Object.assign({}, dependency); // the dependency doesn't have a type so try to infer it from the path if (!dependency.type) { if (dependency.package) { dependency.type = 'package'; dependency.path = dependency.package; delete dependency.package; } else if (dependency.path) { const parsed = parsePath(dependency.path); dependency.type = parsed.type; dependency.path = parsed.path; } else if (dependency.intersection) { dependency.type = 'intersection'; dependency.dependencies = dependency.intersection; delete dependency.intersection; } else if (dependency.dependencies) { dependency.type = 'dependencies'; } } } return dependency; }; } function DependencyRegistry() { this.registeredTypes = {}; this.extensions = {}; this.requireExtensions = {}; this._normalizers = []; this._finalNormalizers = []; this.addNormalizer(createDefaultNormalizer(this)); this.registerDefaults(); this.requireExtensions = {}; this.requireExtensionNames = undefined; } DependencyRegistry.prototype = { __DependencyRegistry: true, registerDefaults: function() { this.registerStyleSheetType('css', require('./dependency-resource')); this.registerJavaScriptType('js', require('./dependency-resource')); this.registerJavaScriptType('comment', require('./dependency-comment')); this.registerPackageType('package', require('./dependency-package')); this.registerPackageType('intersection', require('./dependency-intersection')); this.registerPackageType('dependencies', require('./dependency-dependencies')); this.registerExtension('browser.json', 'package'); this.registerExtension('optimizer.json', 'package'); }, typeForPath: function(path) { // Find the type from the longest matching file extension. // For example if we are trying to infer the type of "jquery-1.8.3.js" then we will try: // a) "8.3.js" // b) "3.js" // c) "js" path = nodePath.basename(path); let type = this.extensions[path]; if (type) { // This is to handle the case where the extension // is the actual filename. For example: "browser.json" return type; } let dotPos = path.indexOf('.'); if (dotPos === -1) { return null; } do { type = path.substring(dotPos + 1); if (hasOwn.call(this.extensions, type)) { return this.extensions[type]; } // move to the next dot position dotPos = path.indexOf('.', dotPos + 1); } while (dotPos !== -1); const lastDot = path.lastIndexOf('.'); return path.substring(lastDot + 1); }, addNormalizer: function(normalizerFunc) { ok(typeof normalizerFunc === 'function', 'function expected'); this._normalizers.unshift(normalizerFunc); // Always run the glob normalizer first this._finalNormalizers = [globNormalizer].concat(this._normalizers); }, registerType: function(type, mixins) { equal(typeof type, 'string', '"type" should be a string'); equal(typeof mixins, 'object', '"mixins" should be a object'); const isPackageDependency = mixins._packageDependency === true; const hasReadFunc = mixins.read; if (isPackageDependency && hasReadFunc) { throw new Error('Manifest dependency of type "' + type + '" is not expected to have a read() method.'); } if (mixins.init) { mixins.doInit = mixins.init; delete mixins.init; } mixins = extend({}, mixins); const properties = mixins.properties || {}; const childProperties = Object.create(Dependency.prototype.properties); extend(childProperties, properties); mixins.properties = childProperties; const calculateKey = mixins.calculateKey; if (calculateKey) { mixins.doCalculateKey = calculateKey; delete mixins.calculateKey; } const getLastModified = mixins.getLastModified || mixins.lastModified; if (getLastModified) { mixins.doGetLastModified = getLastModified; delete mixins.getLastModified; delete mixins.lastModified; } if (!isPackageDependency && mixins.read) { // Wrap the read method to ensure that it always returns a stream // instead of possibly using a callback const oldRead = mixins.read; delete mixins.read; mixins.doRead = function(lassoContext) { return readStream(() => { return oldRead.call(this, lassoContext); }); }; } const _this = this; function Ctor(dependencyConfig, dirname, filename) { this.__dependencyRegistry = _this; Dependency.call(this, dependencyConfig, dirname, filename); } inherit(Ctor, Dependency); extend(Ctor.prototype, mixins); this.registeredTypes[type] = Ctor; }, registerRequireExtension: function(ext, options) { this.requireExtensionNames = undefined; equal(typeof ext, 'string', '"ext" should be a string'); if (ext.charAt(0) === '.') { ext = ext.substring(1); } if (typeof options === 'function') { options = { read: options }; } ok(options.read || options.createReadStream, '"read" or "createReadStream" is required'); this.requireExtensions[ext] = options; }, /** * In addition to registering a require extension using the "registerRequireExtension", * this method also registers a new dependency type with possibly additional properties. * * For example, if you just use "registerRequireExtension('foo', ...)", then only the following is supported: * - var foo = require('./hello.foo'); * - "require: ./hello.foo" * * However, if you use registerRequireType with custom proeprties then all of the following are supported: * - var foo = require('./hello.foo'); * - "require: ./hello.foo" * - "hello.foo", * - { "type": "foo", "path": "hello.foo", "hello": "world" } * * For an example, please see: * https://github.com/lasso-js/lasso-marko/blob/master/lasso-marko-plugin.js * * * dependency that can be required. Howev * @param {String} type The extension/type to register * @param {Object} mixins [description] */ registerRequireType: function(type, options) { equal(typeof type, 'string', '"type" should be a string'); equal(typeof options, 'object', '"options" should be a object'); const userRead = options.read; const userCreateReadStream = options.createReadStream; const userGetLastModified = options.getLastModified; const extensionOptions = extend({}, options); if (userRead) { extensionOptions.read = function(path, lassoContext, callback) { // Chop off the first path argument return userRead.apply(this, slice.call(arguments, 1)); }; } if (userCreateReadStream) { extensionOptions.userCreateReadStream = function(path, lassoContext) { // Chop off the first path argument return userCreateReadStream.apply(this, slice.call(arguments, 1)); }; } if (userGetLastModified) { extensionOptions.getLastModified = async function (path, lassoContext) { // Chop off the first path argument return userGetLastModified.apply(this, slice.call(arguments, 1)); }; } this.registerRequireExtension(type, extensionOptions); this.registerPackageType(type, { properties: { path: 'string' }, async init(lassoContext) { this.path = this.resolvePath(this.path); }, getSourceFile() { return this.path; }, async getDependencies (lassoContext) { const path = this.path; return [ { type: 'require', path } ]; } }); }, getRequireExtensionNames() { if (this.requireExtensionNames === undefined) { const extensionsLookup = {}; // eslint-disable-next-line n/no-deprecated-api const nodeRequireExtensions = require.extensions; for (const ext in nodeRequireExtensions) { if (ext !== '.node') { extensionsLookup[ext] = true; } } for (let ext in this.requireExtensions) { if (ext.charAt(0) !== '.') { ext = '.' + ext; } extensionsLookup[ext] = true; } this.requireExtensionNames = Object.keys(extensionsLookup); } return this.requireExtensionNames; }, createRequireHandler(path, lassoContext, userOptions) { ok(path, '"path" is required'); ok(lassoContext, '"lassoContext" is required'); ok(userOptions, '"userOptions" is required'); ok(typeof path === 'string', '"path" should be a string'); ok(typeof lassoContext === 'object', '"lassoContext" should be an object'); return new RequireHandler(userOptions, lassoContext, path); }, getRequireHandler: function(path, lassoContext) { ok(path, '"path" is required'); ok(lassoContext, '"lassoContext" is required'); ok(typeof path === 'string', '"path" should be a string'); ok(typeof lassoContext === 'object', '"lassoContext" should be an object'); const basename = nodePath.basename(path); const lastDot = basename.lastIndexOf('.'); if (lastDot === -1) { return null; } const ext = basename.substring(lastDot + 1); const userOptions = this.requireExtensions[ext]; if (!userOptions) { return null; } return new RequireHandler(userOptions, lassoContext, path); }, registerJavaScriptType: function(type, mixins) { equal(typeof type, 'string', '"type" should be a string'); equal(typeof mixins, 'object', '"mixins" should be a object'); mixins.contentType = CONTENT_TYPE_JS; this.registerType(type, mixins); }, registerStyleSheetType: function(type, mixins) { equal(typeof type, 'string', '"type" should be a string'); equal(typeof mixins, 'object', '"mixins" should be a object'); mixins.contentType = CONTENT_TYPE_CSS; this.registerType(type, mixins); }, registerPackageType: function(type, mixins) { equal(typeof type, 'string', '"type" should be a string'); equal(typeof mixins, 'object', '"mixins" should be a object'); mixins._packageDependency = true; this.registerType(type, mixins); }, registerExtension: function(extension, type) { equal(typeof extension, 'string', '"extension" should be a string'); equal(typeof type, 'string', '"type" should be a string'); this.extensions[extension] = type; }, getType: function(type) { return this.registeredTypes[type]; }, createDependency: function(config, dirname, filename) { ok(config, '"config" is required'); ok(dirname, '"dirname" is required'); equal(typeof config, 'object', 'Invalid dependency: ' + require('util').inspect(config)); const type = config.type; const Ctor = this.registeredTypes[type]; if (!Ctor) { throw new Error('Dependency of type "' + type + '" is not supported. (dependency=' + require('util').inspect(config) + ', package="' + filename + '"). Registered types:\n' + Object.keys(this.registeredTypes).join(', ')); } return new Ctor(config, dirname, filename); }, async normalizeDependencies (dependencies, dirname, filename) { logger.debug('normalizeDependencies() BEGIN: ', dependencies, 'count:', dependencies.length); if (dependencies.length === 0) { return dependencies; } let i = 0; let j = 0; dependencies = dependencies.concat([]); const normalizers = this._finalNormalizers; const normalizerCount = normalizers.length; const context = { dirname, filename }; function handleNormalizedDependency (dependencies, i, normalizedDependency) { if (normalizedDependency) { if (Array.isArray(normalizedDependency)) { // Remove one dependencies.splice.apply(dependencies, [i, 1].concat(normalizedDependency)); j = 0; // Continue at the same dependency index, but restart normalizing at the beginning return null; } else { dependencies[i] = normalizedDependency; } } j++; return normalizedDependency; }; const handleDependencyNormalization = async () => { while (i < dependencies.length) { let dependency = dependencies[i]; if (!dependency.__Dependency) { if (j < normalizerCount) { const normalizeFunc = normalizers[j]; const normalizedDependency = await normalizeFunc(dependency, context); const handledNormalizedDep = handleNormalizedDependency(dependencies, i, normalizedDependency); dependency = handledNormalizedDep || dependency; // Stop looping and we will pick up where we left off when // the async normalizer finishes return handleDependencyNormalization(); } // Restart with the first normalizer for the next dependency j = 0; // Convert the dependency object to an actual Dependency instance dependency = this.createDependency(dependency, dirname, filename); } dependencies[i] = dependency; i++; } logger.debug('normalizeDependencies() DONE!'); return dependencies; }; return handleDependencyNormalization(); }, /** * This method is used to create a new JavaScript or CSS * type that allows the code to be transformed using a custom * transform function. This was introduced because we wanted to * be able to easily use the babel transpiler on individual * JS dependencies without registering a global transform. */ createResourceTransformType (transformFunc) { const transformType = extend({}, dependencyResource); extend(transformType, { isExternalResource: function() { return false; }, async read (context) { const readResult = await dependencyResource.read.call(this, {}); return new Promise((resolve, reject) => { function callback (err, res) { return err ? reject(err) : resolve(res); } if (typeof readResult === 'string') { return transformFunc(readResult, callback); } else if (readResult) { let code = ''; readResult .on('data', function(data) { code += data; }) .on('end', function() { transformFunc(code, callback); }); } }); } }); return transformType; } }; module.exports = DependencyRegistry; ================================================ FILE: src/dependencies/RequireHandler.js ================================================ const nodePath = require('path'); const ok = require('assert').ok; const EMPTY_ARRAY_PROMISE = Promise.resolve([]); class RequireHandler { constructor(userOptions, lassoContext, path) { ok(userOptions, '"userOptions" is required'); ok(lassoContext, '"lassoContext" is required'); ok(path, '"path" is required'); this.lassoContext = lassoContext; this.userOptions = userOptions; this.path = path; this.includePathArg = true; this.userThisObject = { path, resolvePath: function(pathToResolve) { const dir = nodePath.dirname(path); const resolved = lassoContext.resolve(pathToResolve, dir); return resolved && resolved.path; } }; this.lastModified = null; this.object = userOptions.object === true; } init() { const lassoContext = this.lassoContext; const userInit = this.userOptions.init; return new Promise((resolve, reject) => { if (userInit) { const promise = userInit.call(this.userThisObject, lassoContext, (err) => { if (err) { reject(err); } else { resolve(); } }); if (promise !== undefined) { resolve(promise); } } else { resolve(); } }); } createReadStream() { const lassoContext = this.lassoContext; const path = this.path; const createReadStream = this.userOptions.createReadStream; if (createReadStream) { return this.includePathArg ? createReadStream.call(this.userThisObject, path, lassoContext) : createReadStream.call(this.userThisObject, lassoContext); } const userRead = this.userOptions.read; if (userRead) { return lassoContext.createReadStream((callback) => { return this.includePathArg ? userRead.call(this.userThisObject, path, lassoContext, callback) : userRead.call(this.userThisObject, lassoContext, callback); }); } else { return lassoContext.createReadStream((callback) => { callback(null, ''); }); } } getLastModified() { const lassoContext = this.lassoContext; const path = this.path; const lastModifiedPromise = this.lastModified; if (lastModifiedPromise) { return lastModifiedPromise; } const userLastModified = this.userOptions.getLastModified; if (userLastModified) { this.lastModifiedPromise = new Promise((resolve, reject) => { const callback = (err, lastModified) => { if (err) { reject(err); } else { resolve(lastModified || -1); } }; const userPromise = this.includePathArg ? userLastModified.call(this.userThisObject, path, lassoContext, callback) : userLastModified.call(this.userThisObject, lassoContext, callback); if (userPromise !== undefined) { resolve(userPromise || -1); } }); } else { this.lastModifiedPromise = this.lassoContext.getFileLastModified(path); } return this.lastModifiedPromise; } async getDependencies() { const lassoContext = this.lassoContext; const userGetDependencies = this.userOptions.getDependencies; if (!userGetDependencies) { return EMPTY_ARRAY_PROMISE; } return userGetDependencies.call(this.userThisObject, lassoContext); } getDefaultBundleName(pageBundleName, lassoContext) { const userGetDefaultBundleName = this.userOptions.getDefaultBundleName; if (userGetDefaultBundleName) { return userGetDefaultBundleName.call(this.userThisObject, pageBundleName, lassoContext); } } } module.exports = RequireHandler; ================================================ FILE: src/dependencies/dependency-comment.js ================================================ module.exports = { properties: { path: 'string' }, // we don't actually produce JavaScript or CSS contentType: 'none', read: function(lassoContext) { return null; }, calculateKey () { return 'comment'; } }; ================================================ FILE: src/dependencies/dependency-dependencies.js ================================================ module.exports = { properties: { dependencies: 'array' }, async init (lassoContext) {}, async getDependencies (lassoContext) { return this.dependencies || []; }, calculateKey () { return null; // A just use a unique ID for this dependency } }; ================================================ FILE: src/dependencies/dependency-intersection.js ================================================ const dependencyWalker = require('../dependency-walker'); const DependencyList = require('../DependencyList'); const thresholdRegex = /^(\d+)([%]*)$/; const hasOwn = Object.prototype.hasOwnProperty; function onDependency (tracking, strictIntersection, firstSet, i) { return function (dependency, context) { if (dependency.isPackageDependency()) { return; } const key = dependency.getKey(); const info = tracking[key]; if (info === undefined) { tracking[key] = { dependency, count: 1 }; } else { info.count++; } if ((i === 0) && strictIntersection) { // strict intersection so only need to keep track // dependencies from first set (which is a little // arbitrary but will work) firstSet.push(dependency); } }; } module.exports = { properties: { dependencies: 'array', threshold: 'object' }, async init (lassoContext) { if (!this.dependencies) { throw new Error('"dependencies" property is required'); } if (!Array.isArray(this.dependencies)) { throw new Error('"dependencies" property is required'); } this.dependencies = new DependencyList( this.dependencies, lassoContext.dependencyRegistry, this.getParentManifestDir(), this.getParentManifestPath()); if (this.threshold) { if (typeof this.threshold === 'string') { const match = thresholdRegex.exec(this.threshold); let units; if (!match || ((units = match[2]) && (units !== '%'))) { throw new Error('Invalid threshold: ' + this.threshold); } this.threshold = { value: parseInt(match[1], 10), units }; } else { this.threshold = { value: this.threshold }; } } }, getDir: function() { return null; }, async getDependencies (lassoContext) { const tracking = {}; const flags = lassoContext.flags; const firstSet = []; const dependencies = await this.dependencies.normalize(); const numDependencies = dependencies.length; let thresholdValue; if (this.threshold) { thresholdValue = this.threshold.value; if (this.threshold.units === '%') { // A dependency will become part of the intersection if it is in at X percent of the enumerated list of dependencies thresholdValue = thresholdValue / 100 * numDependencies; } else { // A dependency will become part of the intersection if it is in at least X of the enumerated list of dependencies thresholdValue = this.threshold.value; } } else { // strict intersection -- only include the dependencies that are in the enumerated list of dependencies thresholdValue = numDependencies; } const strictIntersection = (thresholdValue >= numDependencies); for (const [i, dependency] of dependencies.entries()) { // HACK: The built-in `dep-require` dependency type // uses its `Deduper` instance to ignore dependencies // within the same "phase" of a lasso operation. // // However, for the purposes of calculating intersection // we should not de-duplicate across each "walk" of // starting dependency. // // The `Deduper` stores a cache of "visited" dependencies in // `lassoContext.phaseData['dependency-require']`. // // We reset the `phaseData` property to remove this // cache before we walk each starting dependency. const oldPhaseData = lassoContext.phaseData; lassoContext.phaseData = {}; await dependencyWalker.walk({ lassoContext, dependency, flags, on: { dependency: onDependency(tracking, strictIntersection, firstSet, i) } }); lassoContext.phaseData = oldPhaseData; } const intersection = []; function checkDependency(info) { if (info.count >= thresholdValue) { intersection.push(info.dependency); } } if (strictIntersection) { // strict intersection for (let i = 0, len = firstSet.length; i < len; i++) { const dependency = firstSet[i]; checkDependency(tracking[dependency.getKey()]); } } else { // not a strict intersection so we need to check counts for all dependencies for (const key in tracking) { if (hasOwn.call(tracking, key)) { checkDependency(tracking[key]); } } } return intersection; }, calculateKey () { return null; // A just use a unique ID for this dependency } }; ================================================ FILE: src/dependencies/dependency-package.js ================================================ const ok = require('assert').ok; const manifestLoader = require('../manifest-loader'); const nodePath = require('path'); module.exports = { properties: { path: 'string', from: 'string' }, async init (lassoContext) { this._alias = this.path; // Store a reference to the unresolved path const from = this.from || this.getParentManifestDir(); delete this.from; try { this._packageManifest = this.createPackageManifest( manifestLoader.load( this.path, from)); } catch (e) { if (e.fileNotFound) { const inFile = this.getParentManifestPath(); throw new Error('Lasso manifest not found for path "' + this.path + '" referenced in "' + (inFile || this.getParentManifestDir()) + '"'); } else { throw new Error('Unable to load lasso manifest for path "' + this.path + '". Dependency: ' + this.toString() + '. Exception: ' + (e.stack || e)); } } this.path = this._packageManifest.filename; // Store the resolved path and use that as the key ok(this.path, 'this.path should not be null'); this._dir = nodePath.dirname(this.path); }, getDir: function() { return this._dir; }, async loadPackageManifest (lassoContext) { return this._packageManifest; }, calculateKey () { return 'package|' + this.path; } }; ================================================ FILE: src/dependencies/dependency-resource.js ================================================ const nodePath = require('path'); const urlReader = require('../util/url-reader'); const urlRegExp = /^(http:|https:)?\/\//; const fs = require('fs'); function maskDefine(code) { return '(function(define) { /* mask define */ ' + code + '\n}()); // END: mask define wrapper'; } module.exports = { properties: { path: 'string', dir: 'string', virtualPath: 'string', url: 'string', code: 'string', external: 'boolean', 'mask-define': 'boolean' }, async init (lassoContext) { let path = this.path; if (!this.path && !this.url && !this.code && !this.virtualPath) { throw new Error('"path", "virtualPath", "url" or "code" is required for a resource dependency'); } if (urlRegExp.test(path)) { this.url = path; path = null; delete this.path; } if (path) { this.path = this.resolvePath(path); this._dir = nodePath.dirname(this.path); } }, cacheConfig: { cacheable: true, static: true }, getDir: function() { return this._dir || this.dir; }, async read (context) { if (this.code) { return this.code; } // if mask-define, use callback to wrap the resource if (this['mask-define'] === true) { const code = await fs.promises.readFile(this.path, 'utf8'); return maskDefine(code); // otherwise return a stream } else { if (this.url) { return urlReader.createUrlReadStream(this.url); } else { return fs.createReadStream(this.path, { encoding: 'utf8' }); } } }, isExternalResource: function() { return this.url != null && this.external !== false; }, getUrl: function() { if (this.external !== false) { return this.url; } }, getSourceFile: function() { return this.path || this.virtualPath; }, async getLastModified (lassoContext) { if (!this.path) { return -1; } return this.getFileLastModified(this.path); } }; ================================================ FILE: src/dependencies/glob.js ================================================ const nodePath = require('path'); const glob = require('util').promisify(require('glob')); const globRegExp = /[*?+{}]/; async function globNormalizer (dependency, context) { if (typeof dependency === 'string' && globRegExp.test(dependency)) { const typeSeparator = dependency.indexOf(':'); const basedir = context.dirname; let pattern = dependency; let type = null; let matches = []; if (typeSeparator) { type = dependency.substring(0, typeSeparator).trim(); pattern = dependency.substring(typeSeparator + 1); } pattern = pattern.trim(); const patterns = pattern.split(/\s+/); for (const pattern of patterns) { const files = await glob(pattern, { cwd: basedir }); matches = matches.concat(files); } matches = matches.map((match) => { match = nodePath.join(basedir, match); return type ? type + ':' + match : match; }); return matches; } } exports.normalizer = globNormalizer; ================================================ FILE: src/dependencies/index.js ================================================ const Dependency = require('./Dependency'); const DependencyRegistry = require('./DependencyRegistry'); exports.Dependency = Dependency; exports.DependencyRegistry = DependencyRegistry; exports.createRegistry = function() { return new DependencyRegistry(); }; exports.isRegistry = function(o) { return o && o.__DependencyRegistry === true; }; exports.isDependency = function(d) { return d && d.__Dependency === true; }; exports.toString = function () { return '[lasso@' + __filename + ']'; }; ================================================ FILE: src/dependency-walker.js ================================================ const EventEmitter = require('events').EventEmitter; const forEachEntry = require('raptor-util/forEachEntry'); const perfLogger = require('raptor-logging').logger('lasso/perf'); const logger = require('raptor-logging').logger(module); const createError = require('raptor-util/createError'); /** * Helper method to walk all dependencies recursively * * @param options */ async function walk(options) { const startTime = Date.now(); const emitter = new EventEmitter(); const lassoContext = options.lassoContext || {}; const flags = lassoContext.flags; const shouldSkipDependencyFunc = options.shouldSkipDependency; const walkContext = { lassoContext }; const on = options.on; if (!on) { throw new Error('"on" property is required'); } forEachEntry(on, function(event, listener) { emitter.on(event, listener); }); const foundDependencies = {}; async function walkDependencies (dependencies, parentDependency, jsSlot, cssSlot, dependencyChain) { logger.debug('walkDependencies', dependencies); for (const dependency of dependencies) { await walkDependency(dependency, parentDependency, jsSlot, cssSlot, dependencyChain); } } async function walkManifest(manifest, parentDependency, jsSlot, cssSlot, dependencyChain) { delete walkContext.dependency; walkContext.package = manifest; walkContext.dependencyChain = dependencyChain; emitter.emit('manifest', manifest, walkContext, parentDependency); logger.debug('walkManifest', manifest); const dependencies = await manifest.getDependencies({ flags, lassoContext: options.lassoContext }); logger.debug('walkManifest - dependencies', dependencies); await walkDependencies(dependencies, parentDependency, jsSlot, cssSlot, dependencyChain); } async function walkDependency(dependency, parentDependency, jsSlot, cssSlot, dependencyChain) { dependencyChain = dependencyChain.concat(dependency); await dependency.init(lassoContext); logger.debug('dependency init', dependency); if (dependency._condition && !dependency._condition(flags)) { return; } const key = await dependency.calculateKey(lassoContext); if (foundDependencies[key]) { return; } foundDependencies[key] = true; let slot; if (!dependency.isPackageDependency()) { slot = dependency.getSlot(); if (!slot) { if (dependency.isJavaScript()) { slot = jsSlot || 'body'; } else { slot = cssSlot || 'head'; } } } walkContext.slot = slot; delete walkContext.package; walkContext.dependency = dependency; walkContext.parentDependency = parentDependency; walkContext.dependencyChain = dependencyChain; if (shouldSkipDependencyFunc && shouldSkipDependencyFunc(dependency, walkContext)) { return; } emitter.emit('dependency', dependency, walkContext); if (dependency.isPackageDependency()) { try { const dependencyManifest = await dependency.getPackageManifest(lassoContext); if (!dependencyManifest) { return; } await walkManifest( dependencyManifest, dependency, dependency.getJavaScriptSlot() || jsSlot, dependency.getStyleSheetSlot() || cssSlot, dependencyChain); } catch (err) { const message = 'Failed to walk dependency ' + dependency + '. Dependency chain: ' + dependencyChain.join(' → ') + '. Cause: ' + err; throw createError(message, err); } } } function done () { perfLogger.debug('Completed walk in ' + (Date.now() - startTime) + 'ms'); emitter.emit('end'); } const dependencyChain = []; if (options.lassoManifest) { await walkManifest( options.lassoManifest, null, // parent package null, // jsSlot null, dependencyChain); done(); } else if (options.dependency) { await walkDependency( options.dependency, null, // parent package null, // jsSlot null, dependencyChain); done(); } else if (options.dependencies) { const dependencies = await options.dependencies.normalize(); await walkDependencies( dependencies, null, null, null, dependencyChain); done(); } else { throw new Error('"lassoManifest", "dependency", "dependencies" is required'); } } exports.walk = walk; ================================================ FILE: src/flags.js ================================================ const FlagSet = require('./FlagSet'); function isFlagSet(o) { return o && o.__FlagSet; } function createFlagSet(flags) { return new FlagSet(flags); } exports.isFlagSet = isFlagSet; exports.createFlagSet = createFlagSet; ================================================ FILE: src/index.js ================================================ require('raptor-logging'); const nodePath = require('path'); const configLoader = require('./config-loader'); const flags = require('./flags'); const transforms = require('./transforms'); const ok = require('assert').ok; const fs = require('fs'); const raptorCache = require('raptor-cache'); const extend = require('raptor-util/extend'); const getClientPath = require('lasso-modules-client/transport').getClientPath; const stripJsonComments = require('strip-json-comments'); if (!getClientPath) { throw new Error('getClientPath is not defined'); } const configDefaults = { outputDir: 'static', urlPrefix: '/static', includeSlotNames: false, fingerprintsEnabled: true, resolveCssUrls: true, bundlingEnabled: true, minify: false }; exports.defaultConfig = {}; exports.defaultConfigBaseDir = process.cwd(); exports.defaultConfigFilename = null; function create(config, baseDir, filename) { if (!config) { config = exports.defaultConfig; } else if (typeof config === 'string') { filename = config; filename = nodePath.resolve(process.cwd(), filename); baseDir = nodePath.dirname(filename); const json = fs.readFileSync(filename, { encoding: 'utf8' }); config = JSON.parse(stripJsonComments(json)); } if (!baseDir) { baseDir = config.baseDir || process.cwd(); } if (!config.__Config) { config = configLoader.load(config, baseDir, filename, configDefaults); } const Lasso = require('./Lasso'); const theLasso = new Lasso(config); return theLasso; } let defaultLasso = null; let isConfigured = false; function getDefaultLasso() { if (!defaultLasso) { // Fixes #82 - Make lasso a singleton. When resolving the default // lasso use the global lasso unless this lasso // was explicitly configured. if (!isConfigured && global.GLOBAL_LASSO) { defaultLasso = global.GLOBAL_LASSO.getDefaultLasso(); return defaultLasso; } defaultLasso = create( exports.defaultConfig, exports.defaultConfigBaseDir, exports.defaultConfigFilename); } return defaultLasso; } function setDevelopmentMode() { extend(configDefaults, { fingerprintsEnabled: false, bundlingEnabled: false, minify: false }); } const NODE_ENV = process.env.NODE_ENV && process.env.NODE_ENV.toLowerCase(); if (NODE_ENV === 'development' || NODE_ENV === 'dev') { setDevelopmentMode(); } function configure(config, baseDir, filename) { // Fixes #82 - Make lasso a singleton. The first lasso that is // explicitly configured will become the default // global instance. isConfigured = true; if (!global.GLOBAL_LASSO) { global.GLOBAL_LASSO = exports; } // Clear out the default lasso instance since that it will // be recreated with the new config. defaultLasso = null; exports.defaultConfig = config = config || {}; exports.defaultConfigBaseDir = baseDir; exports.defaultConfigFilename = filename; } async function lassoPage(pageConfig) { ok(pageConfig, '"pageConfig" is required'); ok(typeof pageConfig === 'object', '"pageConfig" should be an object'); let dependencies = pageConfig.dependencies; let packagePath = pageConfig.packagePath; ok(dependencies || packagePath, '"page.dependencies" or "page.packagePath" is required'); if (dependencies) { ok(typeof dependencies === 'string' || Array.isArray(dependencies), '"dependencies" should be an Array or a String'); } if (typeof dependencies === 'string') { packagePath = nodePath.resolve(process.cwd(), dependencies); dependencies = null; } else if (typeof packagePath === 'string') { packagePath = nodePath.resolve(process.cwd(), packagePath); } return getDefaultLasso().lassoPage(pageConfig); } async function lassoResource (path, context) { return getDefaultLasso().lassoResource(path, context); } function clearCaches() { raptorCache.freeAll(); require('./caching-fs').clearCaches(); } exports.getDefaultLasso = getDefaultLasso; exports.lassoPage = lassoPage; exports.lassoResource = lassoResource; exports.create = create; exports.configure = configure; exports.setDevelopmentMode = setDevelopmentMode; exports.createFlagSet = flags.createFlagSet; exports.isFlagSet = flags.isFlagSet; exports.transforms = transforms; exports.writers = require('./writers'); exports.flushAllCaches = raptorCache.flushAll; exports.handleWatchedFileChanged = function(path) { console.log('[lasso] File modified: ' + path); clearCaches(); }; exports.clearCaches = clearCaches; exports.getClientPath = getClientPath; Object.defineProperty(exports, 'defaultLasso', { get: getDefaultLasso, enumerable: true, configurable: false }); exports.toString = function () { return '[lasso@' + __filename + ']'; }; require('../browser-refresh').enable(); ================================================ FILE: src/last-modified.js ================================================ const cachingFs = require('./caching-fs'); // TODO: Change in lasso-caching-fs exports.forFile = async function (filePath) { return new Promise((resolve, reject) => { cachingFs.lastModified(filePath, function (err, data) { return err ? reject(err) : resolve(data); }); }); }; ================================================ FILE: src/manifest-loader.js ================================================ const ok = require('assert').ok; const nodePath = require('path'); const Module = require('module').Module; const fs = require('fs'); const stripJsonComments = require('strip-json-comments'); const resolveFrom = require('resolve-from'); const resolveCache = {}; const manifestCache = {}; const isAbsolute = require('./path').isAbsolute; const hasOwn = Object.prototype.hasOwnProperty; const allowedProps = { dependencies: true, async: true, main: true, requireRemap: true }; function readManifest(path) { let manifest = manifestCache[path]; if (manifest !== undefined) { return manifest; } let json; try { json = fs.readFileSync(path, { encoding: 'utf8' }); } catch (e) { manifest = null; } if (json) { try { manifest = JSON.parse(stripJsonComments(json)); } catch (e) { throw new Error('Unable to parse JSON file at path "' + path + '". Exception: ' + e); } for (const k in manifest) { if (hasOwn.call(manifest, k)) { if (!allowedProps[k]) { throw new Error('Invalid property of "' + k + '" in lasso manifest at path "' + path + '"'); } } } if (manifest.main && manifest.dependencies) { throw new Error('"dependencies" not allowed when "main specified. Lasso.js manifest file: ' + path); } manifest.dirname = nodePath.dirname(path); manifest.filename = path; } manifestCache[path] = manifest; return manifest; } function tryManifest(dirname, manifestPath) { let manifest = readManifest(nodePath.resolve(dirname, manifestPath, 'browser.json')); if (!manifest) { manifest = readManifest(nodePath.resolve(dirname, manifestPath, 'optimizer.json')); } return manifest; } function tryExtensionManifest(dirname, manifestPath) { let manifest = readManifest(nodePath.resolve(dirname, manifestPath + '.browser.json')); if (!manifest) { manifest = readManifest(nodePath.resolve(dirname, manifestPath + '.optimizer.json')); } return manifest; } function tryQualified(dirname, manifestPath) { const path = nodePath.resolve(dirname, manifestPath); return readManifest(path); } function tryAll(dirname, manifestPath) { if (manifestPath.endsWith('browser.json') || manifestPath.endsWith('optimizer.json')) { return tryQualified(dirname, manifestPath); // //browser.json } else { return tryManifest(dirname, manifestPath) || // //browser.json tryExtensionManifest(dirname, manifestPath); // /.browser.json } } function _resolve(path, from) { if (isAbsolute(path)) { return tryAll(from, path); } if (!from) { throw new Error('"from" argument is required for non-absolute paths'); } const resolveKey = path + '|' + from; let manifest = resolveCache[resolveKey]; if (manifest !== undefined) { return manifest; } if (process.platform === 'win32') { path = path.replace(/\\/g, '/'); // Replace back slashes with forward slashes } if (path.startsWith('./') || path.startsWith('../')) { // Don't go through the search paths for relative paths manifest = tryAll(from, path); } else { const resolvedPath = (path.endsWith('optimizer.json') || path.endsWith('browser.json')) && resolveFrom(from, path); if (resolvedPath) { manifest = readManifest(resolvedPath); } else { const paths = Module._nodeModulePaths(from); for (let i = 0, len = paths.length; i < len; i++) { const dir = paths[i]; manifest = tryAll(dir, path); if (manifest) { break; } } } } resolveCache[resolveKey] = manifest; return manifest; } function load(path, from) { ok(path, '"path" is required'); ok(typeof path === 'string', '"path" must be a string'); // ok(from, '"from" is required'); // ok(typeof from === 'string', '"from" must be a string'); // Load the lasso manifest and automatically follow "main" // to get the destination lasso package function loadHelper(path, from) { const manifest = _resolve(path, from); if (!manifest) { const e = new Error('Lasso manifest not found: ' + path + '(searching from: ' + from + ')'); e.fileNotFound = path + '@' + from; throw e; } if (manifest.main) { const mainPath = nodePath.resolve(manifest.dirname, manifest.main); return loadHelper(mainPath, manifest.dirname); } else { return manifest; } } return loadHelper(path, from); } exports.load = load; exports.toString = function () { return '[lasso@' + __filename + ']'; }; ================================================ FILE: src/middleware/index.js ================================================ exports.serveStatic = require('./serveStatic'); ================================================ FILE: src/middleware/koa/serveStatic.js ================================================ const lasso = require('../'); const send = require('send'); const extend = require('raptor-util/extend'); function notFound() { this.error(404); } module.exports = function(options) { options = options || {}; const myLasso = options.lasso || lasso.getDefaultLasso(); const config = myLasso.config; const outputDir = config.outputDir; const urlPrefix = config.urlPrefix; let routePrefix = urlPrefix; if (!routePrefix.endsWith('/')) { routePrefix += '/'; } if (!outputDir || !urlPrefix) { return function(req, res, next) { return next(); }; } const sendOptions = { fallthrough: false, redirect: false, index: false }; if (options.sendOptions) { extend(sendOptions, options.sendOptions); } sendOptions.root = outputDir; return function(ctx, next) { const req = ctx.request; const res = ctx.response; const path = req.path; if (!path.startsWith(routePrefix) || (req.method !== 'GET' && req.method !== 'HEAD')) { return next(); } const filePath = path.substring(routePrefix.length); // create send stream const stream = send(req, filePath, sendOptions); // add directory handler stream.on('directory', notFound); // forward errors stream.on('error', function error(err) { res.statusCode = err.statusCode || 500; res.end('Not found: ' + filePath); }); // pipe stream.pipe(res); }; }; ================================================ FILE: src/middleware/serveStatic.js ================================================ const lasso = require('../'); const send = require('send'); const extend = require('raptor-util/extend'); function notFound() { this.error(404); } module.exports = function(options) { options = options || {}; const myLasso = options.lasso || lasso.getDefaultLasso(); const config = myLasso.config; const outputDir = config.outputDir; const urlPrefix = config.urlPrefix; let routePrefix = new URL(urlPrefix, 'file:').pathname; if (!routePrefix.endsWith('/')) { routePrefix += '/'; } if (!outputDir || !urlPrefix) { return function(req, res, next) { return next(); }; } const sendOptions = { fallthrough: false, redirect: false, index: false }; if (options.sendOptions) { extend(sendOptions, options.sendOptions); } sendOptions.root = outputDir; return function(req, res, next) { const path = req.path; if (!path.startsWith(routePrefix) || (req.method !== 'GET' && req.method !== 'HEAD')) { return next(); } const filePath = path.substring(routePrefix.length); // create send stream const stream = send(req, filePath, sendOptions); // add directory handler stream.on('directory', notFound); // forward errors stream.on('error', function error(err) { res.statusCode = err.statusCode || 500; res.end('Not found: ' + filePath); }); // pipe stream.pipe(res); }; }; ================================================ FILE: src/node-require-no-op/index.js ================================================ function requireNoOp(module, filename) { /* no-op */ } function enableForExtension(extension) { if (extension == null) { return; } if (Array.isArray(extension)) { extension.forEach(enableForExtension); return; } if (typeof extension !== 'string') { throw new Error('Expected extension to be a string. Actual: ' + extension); } if (extension.charAt(0) !== '.') { extension = '.' + extension; } require.extensions[extension] = requireNoOp; // eslint-disable-line n/no-deprecated-api } exports.enable = function(extensions) { for (let i = 0; i < arguments.length; i++) { enableForExtension(arguments[i]); } }; ================================================ FILE: src/page-bundles-builder.js ================================================ const PageBundles = require('./PageBundles'); const dependencyWalker = require('./dependency-walker'); const assert = require('assert'); const LassoManifest = require('./LassoManifest'); const LoaderMetadata = require('./LoaderMetadata'); const DependencyTree = require('./DependencyTree'); const logger = require('raptor-logging').logger(module); const bundlingStrategies = require('./bundling-strategies'); const hasOwn = Object.prototype.hasOwnProperty; async function build (options, config, bundleMappings, lassoContext) { // TODO: Change to fully use async/await assert.ok(options, '"options" is required'); assert.ok(config, '"config" is required'); assert.ok(bundleMappings, '"bundleMappings" is required'); assert.ok(lassoContext, '"lassoContext" is required'); const pageName = options.name || options.pageName; const lassoManifest = options.lassoManifest; const flags = lassoContext.flags; assert.ok(pageName, 'page name is required'); assert.ok(typeof pageName === 'string', 'page name should be a string'); assert.ok(lassoManifest, '"lassoManifest" is required'); assert.ok(LassoManifest.isLassoManifest(lassoManifest), '"lassoManifest" is not a valid package'); // eslint-disable-next-line no-async-promise-executor return new Promise(async (resolve, reject) => { function callback (err, data) { return err ? reject(err) : resolve(data); } // this will keep track of async loader metadata const loaderMetadata = lassoContext.loaderMetadata = new LoaderMetadata(); const pageBundles = new PageBundles(); const bundlingStrategyFactory = bundlingStrategies[config.getBundlingStrategy() || 'default']; if (!bundlingStrategyFactory) { throw new Error('Invalid "bundlingStrategy": ' + config.getBundlingStrategy() + '. Expected: ' + Object.keys(bundlingStrategies).join(', ')); } const bundlingStrategy = bundlingStrategyFactory(options, config, bundleMappings, pageBundles, lassoContext); const infoEnabled = logger.isInfoEnabled(); const foundAsyncPackages = {}; // as we discover new manifests with "async" property, we add tasks to work queue // that will be later used to build each async package const buildAsyncPackagesWorkQueue = require('raptor-async/work-queue').create({ // task caused error onTaskError: function(err) { // on error, clear the queue of any tasks buildAsyncPackagesWorkQueue.kill(); callback(err); }, onTaskComplete: function() { if (buildAsyncPackagesWorkQueue.idle()) { // no more work so done callback(null, pageBundles); } } }, async function (task, callback) { // this function is called when we need to run task const asyncPackageName = task.asyncPackageName; const debugTree = logger.isDebugEnabled() ? new DependencyTree() : null; // register this async package name in loader metadata loaderMetadata.addAsyncPackageName(asyncPackageName); // Since we are building the async bundles in parallel we need to create a new // lasso context object, each with its own phaseData const nestedLassoContext = Object.create(lassoContext); nestedLassoContext.phaseData = {}; if (infoEnabled) { logger.info('Building async package "' + asyncPackageName + '"...'); } try { await dependencyWalker.walk({ dependencies: task.dependencies, flags, lassoContext: nestedLassoContext, on: { // as we're building async packages, we need to watch for new async packages manifest: handleManifest, dependency: function(dependency, walkContext) { if (dependency.isPackageDependency()) { if (debugTree) { debugTree.add(dependency, walkContext.parentDependency); } return; } if (!dependency.read) { logger.debug('Ignoring dependency because it is not readable', dependency); return; } // NOTE: walkContext will contain everything that is interesting including the following: // - dependency // - slot // - dependencyChain lassoContext.emit('beforeAddDependencyToAsyncPageBundle', walkContext); const bundle = bundlingStrategy.getBundleForAsyncDependency(dependency, walkContext, debugTree); if (bundle) { dependency.onAddToAsyncPageBundle(bundle, lassoContext); pageBundles.addAsyncBundle(bundle); loaderMetadata.addBundle(asyncPackageName, bundle); } } } }); // we're done walking async dependencies for this async package if (debugTree) { logger.debug('Async page bundles for "' + asyncPackageName + '":\n' + debugTree.bundlesToString()); } } catch (err) { logger.error('Error building bundles for async package "' + asyncPackageName + '"', err); return callback(err); } logger.info('Built async package "' + asyncPackageName + '".'); callback(); }); // When walking a manifest, we need to check if the manifest has an "async" property // which declares asynchronous packages function handleManifest(manifest, walkContext) { const async = manifest.async; if (async) { // create jobs to build each async package for (const asyncName in async) { if (hasOwn.call(async, asyncName)) { if (!hasOwn.call(foundAsyncPackages, asyncName)) { foundAsyncPackages[asyncName] = true; buildAsyncPackagesWorkQueue.push({ asyncPackageName: asyncName, dependencies: async[asyncName] }); } } } } } /********************************************************************** * STEP 1: Put all of the dependencies into bundles and keep track of * * packages that are asynchronous. * **********************************************************************/ async function buildSyncPageBundles () { const debugTree = logger.isDebugEnabled() ? new DependencyTree() : null; lassoContext.setPhase('page-bundle-mappings'); await dependencyWalker.walk({ lassoManifest, flags, lassoContext, on: { manifest: handleManifest, dependency: function(dependency, walkContext) { if (dependency.isPackageDependency()) { if (debugTree) { debugTree.add(dependency, walkContext.parentDependency); } // We are only interested in the actual dependencies (not ones that resolve to more dependencies) return; } if (!dependency.read) { return; } // NOTE: walkContext will contain everything that is interesting including the following: // - dependency // - slot // - dependencyChain lassoContext.emit('beforeAddDependencyToSyncPageBundle', walkContext); const bundle = bundlingStrategy.getBundleForSyncDependency(dependency, walkContext, debugTree); if (bundle) { // We know that the dependency is now actually part of the page // (it wasn't just mapped into a bundle during configuration) dependency.onAddToPageBundle(bundle, lassoContext); // At this point we have added the dependency to a bundle and we know the bundle is not asynchronous pageBundles.addSyncBundle(bundle); } } } }); if (debugTree) { logger.debug('Page bundles:\n' + debugTree.bundlesToString()); } } try { await buildSyncPageBundles(); } catch (err) { return callback(err); } lassoContext.setPhase('async-page-bundle-mappings'); if (buildAsyncPackagesWorkQueue.idle()) { callback(null, pageBundles); } else { // start the work to build bundles for async packages buildAsyncPackagesWorkQueue.resume(); } }); } exports.build = build; ================================================ FILE: src/path.js ================================================ // Copyright Joyent, Inc. and other Node contributors. // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. // The following code was adapted from: https://github.com/joyent/node/blob/master/lib/path.js // Older versions of Node.js do not export isAbsolute() so we added it here function absUnix (p) { return p.charAt(0) === '/' || p === ''; } function absWin (p) { if (absUnix(p)) return true; // pull off the device/UNC bit from a windows path. // from node's lib/path.js const splitDeviceRe = /^([a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/]+[^\\\/]+)?([\\\/])?([\s\S]*?)$/; const result = splitDeviceRe.exec(p); const device = result[1] || ''; const isUnc = device && device.charAt(1) !== ':'; const isAbsolute = !!result[2] || isUnc; // UNC paths are always absolute return isAbsolute; } const isAbsolute = process.platform === 'win32' ? absWin : absUnix; exports.isAbsolute = isAbsolute; ================================================ FILE: src/plugins/lasso-image/index.js ================================================ const imageSize = require('util').promisify(require('image-size')); const nodePath = require('path'); const IMAGE_SIZE_WHITELIST = { '.png': true, '.jpeg': true, '.jpg': true, '.gif': true, '.webp': true }; function plugin(lasso, config) { const handler = { properties: { path: 'string' }, async init (lassoContext) { if (!this.path) { throw new Error('"path" is required for a Marko dependency'); } this.path = this.resolvePath(this.path); }, object: true, // We are exporting a simple JavaScript object read (lassoContext) { return new Promise((resolve, reject) => { plugin.getImageInfo(this.path, { lasso, lassoContext }, (err, imageInfo) => { return err ? reject(err) : resolve(JSON.stringify(imageInfo)); }); }); }, async getLastModified(lassoContext) { return lassoContext.getFileLastModified(this.path); } }; [ 'png', 'jpeg', 'jpg', 'gif', 'svg', 'webp' ].forEach(function(ext) { lasso.dependencies.registerRequireType(ext, handler); }); }; plugin.getImageInfo = function(path, options, callback) { if (typeof options === 'function') { callback = options; options = null; } let theLasso; let lassoContext; let renderContext; if (options) { theLasso = options.lasso; lassoContext = options.lassoContext; renderContext = options.renderContext; } if (!theLasso) { theLasso = (plugin.lasso || require('../../../')).defaultLasso; } if (!lassoContext) { lassoContext = theLasso.createLassoContext( renderContext ? { data: { renderContext } } : {}); } // NOTE: lassoContext.getFileLastModified caches file timestamps lassoContext.getFileLastModified(path) .then((lastModified) => { const cache = lassoContext.cache.getCache('lasso-image'); return cache.get(path, { lastModified, async builder () { const imageInfo = {}; const resourceInfo = await theLasso.lassoResource(path, lassoContext); imageInfo.url = resourceInfo.url; const ext = nodePath.extname(path); if (IMAGE_SIZE_WHITELIST[ext]) { const dimensions = await imageSize(path); imageInfo.width = dimensions.width; imageInfo.height = dimensions.height; } return imageInfo; } }); }).then((imageInfo) => { callback(null, imageInfo); }) .catch((err) => { callback(err); }); }; module.exports = plugin; ================================================ FILE: src/plugins/lasso-image/lasso-image-browser.js ================================================ exports.getImageInfo = function(path, options, callback) { if (typeof options === 'function') { callback = options; options = null; } callback(null, require(path)); }; ================================================ FILE: src/plugins/lasso-image/package.json ================================================ { "browser": { "./index.js": "./lasso-image-browser.js" } } ================================================ FILE: src/plugins/lasso-minify-css/index.js ================================================ const CleanCSS = require('clean-css'); function isInline(lassoContext) { if (lassoContext.inline === true) { return true; } if (lassoContext.dependency && lassoContext.dependency.inline === true) { return true; } return false; } module.exports = function (lasso, pluginConfig) { lasso.addTransform({ contentType: 'css', name: module.id, stream: false, transform (code, lassoContext) { if (pluginConfig.inlineOnly === true && !isInline(lassoContext)) { // Skip minification when we are not minifying inline code return code; } // Imports should not be loaded in. This was the same behavior as // sqwish. pluginConfig.processImport = false; return new CleanCSS(pluginConfig).minify(code).styles; } }); }; ================================================ FILE: src/plugins/lasso-minify-js/index.js ================================================ const Terser = require('terser'); const codeFrame = require('@babel/code-frame'); const hasOwn = Object.prototype.hasOwnProperty; function isInline(lassoContext) { if (lassoContext.inline === true) { return true; } if (lassoContext.dependency && lassoContext.dependency.inline === true) { return true; } return false; } module.exports = function (lasso, pluginConfig) { lasso.addTransform({ contentType: 'js', name: module.id, stream: false, transform: async function(code, lassoContext) { if (pluginConfig.inlineOnly === true && !isInline(lassoContext)) { // Skip minification when we are not minifying inline code return code; } const minifyOptions = {}; for (const key in pluginConfig) { if (key !== 'inlineOnly' && hasOwn.call(pluginConfig, key)) { minifyOptions[key] = pluginConfig[key]; } } try { const minified = (await Terser.minify(code, minifyOptions)).code; if (minified && !minified.endsWith(';')) { return minified + ';'; } return minified; } catch (e) { if (e.line) { const dependency = lassoContext.dependency; const frame = codeFrame(code, e.line, e.col, { highlightCode: true }); console.error(e.message + ' in ' + dependency + ' at line ' + e.line + ' column ' + e.col + ':\n' + frame); return code; } else { throw e; } } } }); }; ================================================ FILE: src/plugins/lasso-resolve-css-urls/index.js ================================================ const cssParser = require('raptor-css-parser'); const nodePath = require('path'); const lassoResolveFrom = require('lasso-resolve-from'); const REQUIRE_PREFIX = 'require:'; async function defaultUrlResolver (url, lassoContext) { if (url.indexOf('//') !== -1) { return url; } const queryStart = url.indexOf('?'); let query; let target = url; if (queryStart !== -1) { query = url.substring(queryStart + 1); target = url.substring(0, queryStart); } if (target.charAt(0) === '/' && target.charAt(1) !== '/') { target = nodePath.join(lassoContext.getProjectRoot(), target); } else if (target.startsWith(REQUIRE_PREFIX)) { target = target.substring(REQUIRE_PREFIX.length).trim(); let from; if (lassoContext.dependency) { from = lassoContext.dependency.getDir(lassoContext); } else { from = lassoContext.getProjectRoot(); } const resolved = lassoResolveFrom(from, target); if (resolved) { target = resolved.path; } else { const err = new Error('Module not found: ' + target + ' (from: ' + from + ')'); err.target = target; err.from = from; err.code = 'MODULE_NOT_FOUND'; throw err; } } if (query) { // Add back the query string target += '?' + query; } return target; } function replaceUrls (code, lassoContext, urlResolver) { return new Promise((resolve, reject) => { const lasso = lassoContext.lasso; cssParser.replaceUrls( code, // the replacer function async function (url, start, end, callback) { try { // add exception for css properies with hash e.g. behavior: url(#default#VML); if (url.startsWith('#')) { return callback(null, url); } const resolvedUrl = await urlResolver(url, lassoContext); const bundledResource = await lasso.lassoResource(resolvedUrl, { lassoContext }); callback(null, bundledResource && bundledResource.url); } catch (err) { callback(err); } }, // when everything is done function (err, code) { return err ? reject(err) : resolve(code); }); }); } module.exports = function (lasso, pluginConfig) { const urlResolver = pluginConfig.urlResolver || defaultUrlResolver; lasso.addTransform({ contentType: 'css', name: module.id, // true: The transform function will RECEIVE and RETURN a stream that can be used to read the transformed out // false: The transform function will RECEIVE full code and RETURN a value or promise stream: false, async transform (code, lassoContext) { const dependency = lassoContext.dependency; if (dependency && dependency.resolveCssUrlsEnabled === false) { return code; } return replaceUrls(code, lassoContext, urlResolver); } }); }; ================================================ FILE: src/reader.js ================================================ const transforms = require('./transforms'); const through = require('through'); const logger = require('raptor-logging').logger(module); const ok = require('assert').ok; const fs = require('fs'); const CombinedStream = require('./util/CombinedStream'); const DeferredReadable = require('./util/DeferredReadable'); const nodePath = require('path'); const AsyncValue = require('raptor-async/AsyncValue'); function createReadDependencyStream(dependency, lassoContext, transformerAsyncValue) { const deferredReadable = new DeferredReadable(); transformerAsyncValue.done(function(err, transformer) { if (err) { deferredReadable.emit('error', err); return; } const contentType = dependency.getContentType(); const readContext = Object.create(lassoContext || {}); readContext.contentType = contentType; readContext.dependency = dependency; readContext.transformer = transformer; readContext.dir = dependency.getDir ? dependency.getDir(lassoContext) : null; if (dependency.getSourceFile) { readContext.path = dependency.getSourceFile(); } function createReadStream() { let err; const readStream = dependency.read(readContext); if (!readStream) { err = new Error('Dependency did not return read stream: ' + dependency); } if (typeof readStream.pipe !== 'function') { err = new Error('Dependency returned invalid stream: ' + dependency); } if (err) { return new DeferredReadable(function() { this.emit('error', err); this.push(null); }); } return readStream; } function createTransformedStream(readStream) { if (!transformer.hasTransforms()) { // simply return the dependency read stream if there are no transforms return readStream; } return transformer.transform( readStream, readContext); } const cache = lassoContext.cache; const cacheKey = dependency.getReadCacheKey(); if (cache && dependency.shouldCache(lassoContext) && cacheKey) { const readCache = cache.readCache; dependency.getLastModified(lassoContext) .then((lastModified) => { if (!lastModified || lastModified <= 0) { // This dependency does not support caching // so don't go through the caching layer deferredReadable.setWrappedStream(createTransformedStream(createReadStream())); return; } const cachedReadStream = readCache.createReadStream( cacheKey, { lastModified, builder: function () { // The read dependency has not been cached return Promise.resolve(createReadStream); } }); deferredReadable.setWrappedStream(createTransformedStream(cachedReadStream)); }); } else { deferredReadable.setWrappedStream(createTransformedStream(createReadStream())); } }); return deferredReadable; } function createReadBundleStream(bundle, lassoContext, transformerAsyncValue) { const combinedStream = new CombinedStream({ separator: '\n' }); if (!bundle.hasContent()) { return combinedStream; } let curIndex; let timeoutId; let timeout = lassoContext.config.getBundleReadTimeout(); if (timeout == null) { timeout = exports.DEFAULT_READ_TIMEOUT; } logger.info('Bundle read timeout value: ' + timeout); const dependencies = bundle.getDependencies(); const len = dependencies.length; combinedStream.on('beginStream', function(event) { curIndex = event.index; const dependency = event.stream._dependency; logger.debug('(' + (curIndex + 1) + ' of ' + len + ')', 'Begin reading dependency: ', dependency.toString()); if (timeout > 0) { timeoutId = setTimeout(onTimeout, timeout); } }); combinedStream.on('error', function() { if (timeoutId) { clearTimeout(timeoutId); } }); combinedStream.on('endStream', function(event) { if (timeoutId) { clearTimeout(timeoutId); } const dependency = event.stream._dependency; logger.debug('(' + (curIndex + 1) + ' of ' + len + ')', 'Completed reading dependency: ', dependency.toString()); }); logger.debug('Reading bundle: ' + bundle.getKey()); for (let i = 0; i < len; i++) { const dependency = dependencies[i]; if (dependency && dependency.hasContent() && !dependency.isExternalResource(lassoContext)) { // Each transform needs its own lassoContext since we update the lassoContext with the // current dependency and each dependency is transformed in parallel const readContext = Object.create(lassoContext || {}); readContext.dependency = dependency; readContext.bundle = bundle; const stream = createReadDependencyStream(dependency, readContext, transformerAsyncValue); // tag the stream with the dependency stream._dependency = dependency; combinedStream.addStream(stream); } } function onTimeout() { const dependency = dependencies[curIndex]; const message = 'Reading dependency timed out after ' + timeout + 'ms: ' + dependency.toString() + '. The timeout value can be set via the bundleReadTimeout configuration option (defaults to ' + exports.DEFAULT_READ_TIMEOUT + ').'; combinedStream.emit('error', new Error(message)); combinedStream.forEachStream(function(stream) { if (stream.end) { stream.end(); } }); } return combinedStream; } function createBundleReader(bundle, lassoContext) { ok(bundle, 'bundle is required'); ok(lassoContext, 'lassoContext is required'); const transformContext = Object.create(lassoContext || {}); transformContext.contentType = bundle.contentType; // TODO: Change to fully use async/await const transformerAsyncValue = new AsyncValue(); transforms.createTransformer(lassoContext.config.getTransforms(), transformContext) .then((transformer) => { transformerAsyncValue.resolve(transformer); }) .catch((err) => { transformerAsyncValue.reject(err); }); return { readBundle: function() { return createReadBundleStream(bundle, lassoContext, transformerAsyncValue); }, readDependency: function(dependency) { ok(dependency, 'dependency is required'); ok(typeof dependency.read === 'function', 'Invalid dependency'); return createReadDependencyStream(dependency, lassoContext, transformerAsyncValue); }, async readBundleFully () { if (!bundle.hasContent()) return ''; return new Promise((resolve, reject) => { let hasError = false; function handleError(e) { if (hasError) { return; } hasError = true; reject(e); } const input = this.readBundle(); let code = ''; const captureStream = through( function write (data) { code += data; }, function end () { if (hasError) { return; } resolve(code); }); input.on('error', handleError); captureStream.on('error', handleError); input.pipe(captureStream); }); } }; } function createResourceReader(path, lassoContext) { return { readResource (options) { const readStream = fs.createReadStream(path, options); const filename = nodePath.basename(path); // Use the file extension as the content type const contentType = filename.substring(filename.lastIndexOf('.') + 1); const transformContext = Object.create(lassoContext || {}); transformContext.contentType = contentType; transformContext.path = path; transformContext.dir = nodePath.dirname(path); const readable = new DeferredReadable(); transforms.createTransformer(lassoContext.config.getTransforms(), transformContext) .then((transformer) => { if (transformer.hasTransforms() === false) { // simply use the input stream since there are no transforms after the filtering readable.setWrappedStream(readStream); return; } readable.setWrappedStream(transformer.transform( readStream, transformContext)); }) .catch((err) => { readable.emit('error', err); }); return readable; } }; } exports.DEFAULT_READ_TIMEOUT = 10000; exports.readBundle = createReadBundleStream; exports.createBundleReader = createBundleReader; exports.createResourceReader = createResourceReader; ================================================ FILE: src/require/build-plugin-config.js ================================================ const ok = require('assert').ok; const Transforms = require('./util/Transforms'); const extend = require('raptor-util').extend; const defaultGlobals = { jquery: ['$', 'jQuery'] }; const lassoModulesClientTransport = require('lasso-modules-client/transport'); const getClientPath = lassoModulesClientTransport.getClientPath; const lassoResolveFrom = require('lasso-resolve-from'); function resolveGlobals(config) { const globals = {}; Object.keys(defaultGlobals).forEach(function(moduleName) { let varNames = defaultGlobals[moduleName]; const resolved = lassoResolveFrom(config.rootDir, moduleName); if (resolved) { if (!Array.isArray(varNames)) { varNames = [varNames]; } globals[resolved.path] = varNames; } }); if (config.globals) { extend(globals, config.globals); } config.globals = globals; } function buildPluginConfig(userConfig, defaultProjectRoot) { const config = userConfig ? extend({}, userConfig) : {}; config.rootDir = config.rootDir || defaultProjectRoot || process.cwd(); ok(config.rootDir, '"rootDir" is required'); config.runImmediately = config.runImmediately === true; config.getClientPath = getClientPath; if (config.transforms) { if (config.transforms.length > 0) { config.transforms = new Transforms(config.transforms, defaultProjectRoot); } else { config.transforms = undefined; } } resolveGlobals(config); if (config.modulesRuntimeGlobal) { if (!config.unbundledTargetPrefix) { // Use the modules global variable name as the unbundled // target prefix (it will get sanitized later) config.unbundledTargetPrefix = config.modulesRuntimeGlobal; } // Sanitize the global variable name config.modulesRuntimeGlobal = config.modulesRuntimeGlobal.replace(/[^a-zA-Z0-9\_\$]+/g, '_'); } else { // Use empty string simply because this used as part of the read // cache key for "commonjs-def" dependencies. config.modulesRuntimeGlobal = ''; } let prefix; if ((prefix = config.unbundledTargetPrefix)) { // Build a friendly looking prefix which is used to create // nested directories when module output files are not bundled. prefix = prefix.replace(/[^a-zA-Z0-9\_]+/g, '-'); // remove any leading and trailing "-" characters that may // have been created and store the result config.unbundledTargetPrefix = prefix.replace(/^-+/, '').replace(/-+$/, ''); } config.extensions = userConfig.extensions || ['.js']; return config; } module.exports = buildPluginConfig; ================================================ FILE: src/require/dep-require-remap.js ================================================ const nodePath = require('path'); function create(config, lasso) { return { properties: { from: 'string', to: 'string', fromDirname: 'string' }, async init (lassoContext) { const fromPath = this.resolvePath(this.from); const toPath = this.resolvePath(this.to); this.from = fromPath; this.to = toPath; }, calculateKey () { return this.from + '|' + this.to; }, getDir: function() { return nodePath.dirname(this.to); }, async getDependencies (lassoContext) { return [ { type: 'commonjs-remap', from: this.from, to: this.to } ]; } }; } exports.create = create; ================================================ FILE: src/require/dep-require.js ================================================ const nodePath = require('path'); const ok = require('assert').ok; const equal = require('assert').equal; const VAR_REQUIRE_PROCESS = 'process=require("process")'; const VAR_REQUIRE_BUFFER = 'Buffer=require("buffer").Buffer'; const inspectCache = require('./inspect-cache'); const Deduper = require('./util/Deduper'); const normalizeMain = require('lasso-modules-client/transport').normalizeMain; const lassoCachingFS = require('lasso-caching-fs'); const lassoPackageRoot = require('lasso-package-root'); const normalizeFSPath = require('./util/normalizeFSPath'); const crypto = require('crypto'); function buildAsyncInfo(path, asyncBlocks, lassoContext) { if (asyncBlocks.length === 0) { return null; } const key = 'require-async|' + normalizeFSPath(path); let asyncInfo = lassoContext.data[key]; if (!asyncInfo) { asyncInfo = lassoContext.data[key] = { asyncMeta: {}, asyncBlocks }; asyncBlocks.forEach(function(asyncBlock, i) { if (!asyncBlock.hasInlineDependencies && !asyncBlock.hasFunctionBody) { // only generate a unique package name if the async // function call was provided a `function` as the // last argument or if an array of dependencies were // inlined. return; } const hash = '_' + crypto.createHash('sha1') .update(key) .update(String(i)) .digest('hex') .substring(0, 6); const name = asyncBlock.name = hash; asyncInfo.asyncMeta[name] = asyncBlock.dependencies; }); } return asyncInfo; } function create(config, lasso) { config = config || {}; const globals = config.globals; const getClientPath = config.getClientPath; const readyDependency = lasso.dependencies.createDependency({ type: 'commonjs-ready', inline: 'end', slot: config.lastSlot }, __dirname); const runtimeDependency = lasso.dependencies.createDependency({ type: 'commonjs-runtime' }, __dirname); function handleMetaRemap(metaEntry, deduper) { const from = metaEntry.from; const to = metaEntry.to; const remapKey = deduper.remapKey(from, to); if (!deduper.hasRemap(remapKey)) { const fromPath = getClientPath(from); let toPath; if (to === false) { toPath = false; } else { toPath = getClientPath(to); } deduper.addDependency(remapKey, { type: 'commonjs-remap', from: fromPath, to: toPath, fromFile: from }); } } function handleMetaInstalled(metaEntry, deduper) { const packageName = metaEntry.packageName; const searchPath = metaEntry.searchPath; const fromDir = metaEntry.fromDir; const basename = nodePath.basename(searchPath); if (basename === 'node_modules') { const childName = packageName; const parentPath = lassoPackageRoot.getRootDir(fromDir); const pkg = lassoCachingFS.readPackageSync(nodePath.join(searchPath, packageName, 'package.json')); const childVersion = (pkg && pkg.version) || '0'; const key = deduper.installedKey(parentPath, childName, childVersion); if (!deduper.hasInstalled(key)) { const clientParentPath = getClientPath(parentPath); deduper.addDependency(key, { type: 'commonjs-installed', parentPath: clientParentPath, childName, childVersion, parentDir: parentPath }); } } else { const key = deduper.searchPathKey(searchPath); if (!deduper.hasSearchPath(key)) { let clientSearchPath = getClientPath(searchPath); if (!clientSearchPath.endsWith('/')) { // Search paths should always end with a forward slash clientSearchPath += '/'; } // This is a non-standard standard search path entry deduper.addDependency(key, { type: 'commonjs-search-path', path: clientSearchPath }); } } } function handleMetaMain(metaEntry, deduper) { const dir = metaEntry.dir; const main = metaEntry.main; const key = deduper.mainKey(dir, main); if (!deduper.hasMain(key)) { const dirClientPath = getClientPath(metaEntry.dir); const relativePath = normalizeMain(metaEntry.dir, metaEntry.main); deduper.addDependency(key, { type: 'commonjs-main', dir: dirClientPath, main: relativePath, _sourceFile: metaEntry.main }); } } function handleMetaBuiltin(metaEntry, deduper) { const name = metaEntry.name; const target = metaEntry.target; const key = deduper.builtinKey(name, target); if (!deduper.hasBuiltin(key)) { const targetClientPath = getClientPath(metaEntry.target); deduper.addDependency(key, { type: 'commonjs-builtin', name, target: targetClientPath, _sourceFile: metaEntry.target }); } } function handleMeta(meta, deduper) { for (let i = 0; i < meta.length; i++) { const metaEntry = meta[i]; switch (metaEntry.type) { case 'remap': handleMetaRemap(metaEntry, deduper); break; case 'installed': handleMetaInstalled(metaEntry, deduper); break; case 'main': handleMetaMain(metaEntry, deduper); break; case 'builtin': handleMetaBuiltin(metaEntry, deduper); break; default: throw new Error('Unsupported meta entry: ' + JSON.stringify(metaEntry)); } } } return { properties: { path: 'string', from: 'string', run: 'boolean', wait: 'boolean', resolved: 'object', virtualModule: 'object', requireHandler: 'object' }, async init (lassoContext) { if (!this.path && !this.virtualModule) { const error = new Error('Invalid "require" dependency. "path" property is required'); console.error(module.id, error.stack, this); throw error; } if (!this.resolved) { const virtualModule = this.virtualModule; if (virtualModule) { const path = virtualModule.path || this.path; ok(path, '"path" is required for a virtual module'); this.path = path; if (!this.from) { this.from = nodePath.dirname(path); } const clientPath = virtualModule.clientPath || getClientPath(path); this.resolved = { path, clientPath }; } else if (this.path) { const from = this.from = this.from || this.getParentManifestDir(); const path = this.path; const fromFile = this.getParentManifestPath(); const fromFileRelPath = fromFile ? nodePath.relative(process.cwd(), fromFile) : '(unknown)'; this.resolved = lassoContext.resolveCached(path, from); if (!this.resolved) { throw new Error('Module not found: ' + path + ' (from "' + from + '" and referenced in "' + fromFileRelPath + '")'); } this.meta = this.resolved.meta; } } if (this.run === true) { if (this.wait == null && config.runImmediately === true) { this.wait = false; } this.wait = this.wait !== false; } ok(this.path); }, toString: function() { return '[require: ' + this.path + ']'; }, calculateKey() { // This is a unique key that prevents the same dependency from being // added to the dependency graph repeatedly let key = 'modules-require:' + this.path + '@' + this.from; if (this.run) { key += '|run'; if (this.wait === false) { key += '|wait'; } } return key; }, getDir: function() { return nodePath.dirname(this.resolved.path); }, getRequireHandler: function(lassoContext) { const resolved = this.resolved; let requireHandler = this.requireHandler; if (!requireHandler) { const virtualModule = this.virtualModule; if (virtualModule) { const virtualPath = resolved.path || virtualModule.path || ''; requireHandler = lassoContext.dependencyRegistry.createRequireHandler(virtualPath, lassoContext, virtualModule); requireHandler.includePathArg = false; } } if (!requireHandler) { requireHandler = lassoContext.dependencyRegistry.getRequireHandler(resolved.path, lassoContext); if (!requireHandler) { return null; } } const transforms = config.transforms; if (transforms) { const defaultCreateReadStream = requireHandler.createReadStream.bind(requireHandler); const transformedRequireHandler = Object.create(requireHandler); transformedRequireHandler.createReadStream = function createdTransformedReadStream() { const inStream = defaultCreateReadStream(); return transforms.apply(resolved.path, inStream, lassoContext); }; return transformedRequireHandler; } else { return requireHandler; } }, async getDependencies (lassoContext) { ok(lassoContext, '"lassoContext" argument expected'); // the array of dependencies that we will be returning const dependencies = []; const deduper = new Deduper(lassoContext, dependencies); // Include client module system if needed and we haven't included it yet if (config.includeClient !== false) { deduper.addRuntime(runtimeDependency); } if (this.meta) { handleMeta(this.meta, deduper); } if (!lassoContext.isAsyncBundlingPhase()) { // Add a dependency that will trigger all of the deferred // run modules to run once all of the code has been loaded // for the page deduper.addReady(readyDependency); } const resolved = this.resolved; if (resolved.voidRemap) { // This module has been remapped to a void/empty object // because it is a server-side only module. Nothing // else to do. return dependencies; } const run = this.run === true; const wait = this.wait !== false; if (resolved.type) { // This is not really a require dependency since a type was provided dependencies.push({ type: resolved.type, path: resolved.path }); return dependencies; } const requireHandler = this.getRequireHandler(lassoContext); if (!requireHandler) { // This is not really a dependency that compiles down to a CommonJS module // so just add it to the dependency graph dependencies.push(resolved.path); return dependencies; } const dirname = nodePath.dirname(resolved.path); return requireHandler.init() .then(() => { if (requireHandler.getDependencies) { // A require extension can provide its own `getDependencies` method to provide // additional dependencies beyond what will automatically be discovered using // static code analysis on the CommonJS code associated with the dependency. return requireHandler.getDependencies().then((additionalDependencies) => { if (additionalDependencies && additionalDependencies.length) { additionalDependencies.forEach((dep) => { dependencies.push(dep); }); } }); } }) .then(() => { // Fixes https://github.com/lasso-js/lasso-require/issues/21 // Static JavaScript objects should not need to be inspected if (requireHandler.object) { // Don't actually inspect the required module if it is an object. // For example, this would be the case with require('./foo.json') return requireHandler.getLastModified() .then((lastModified) => { return { createReadStream: requireHandler.createReadStream.bind(requireHandler), lastModified }; }); } return inspectCache.inspectCached( resolved.path, requireHandler, lassoContext, config); }) .then((inspectResult) => { let asyncMeta; let asyncBlocks; if (inspectResult && inspectResult.asyncBlocks && inspectResult.asyncBlocks.length) { const asyncInfo = buildAsyncInfo(resolved.path, inspectResult.asyncBlocks, lassoContext); if (asyncInfo) { asyncBlocks = asyncInfo.asyncBlocks; asyncMeta = asyncInfo.asyncMeta; } } // require was for a source file let additionalVars; ok(inspectResult, 'inspectResult should not be null'); // the requires that were read from inspection (may remain undefined if no inspection result) const requires = inspectResult.requires; if (inspectResult.foundGlobals && (inspectResult.foundGlobals.process || inspectResult.foundGlobals.Buffer)) { additionalVars = []; const addGlobalVar = (path, varCode) => { const resolved = lassoContext.resolve(path, dirname); if (resolved.meta) { handleMeta(resolved.meta, deduper); } const requireKey = deduper.requireKey(path, dirname); if (!deduper.hasRequire(requireKey)) { deduper.addDependency(requireKey, { type: 'require', path, from: dirname }); } additionalVars.push(varCode); }; if (inspectResult.foundGlobals.process) { addGlobalVar('process', VAR_REQUIRE_PROCESS); } if (inspectResult.foundGlobals.Buffer) { addGlobalVar('buffer', VAR_REQUIRE_BUFFER); } } ok(inspectResult.createReadStream, 'createReadStream expected after inspectResult'); ok(inspectResult.lastModified, 'lastModified expected after inspectResult'); equal(typeof inspectResult.lastModified, 'number', 'lastModified should be a number'); const globalVars = globals ? globals[this.resolved.path] : null; // Also check if the directory has an browser.json and if so we should include that as well let lassoJsonPath = nodePath.join(dirname, 'browser.json'); if (lassoContext.cachingFs.existsSync(lassoJsonPath)) { dependencies.push({ type: 'package', path: lassoJsonPath }); } else { lassoJsonPath = nodePath.join(dirname, 'optimizer.json'); if (lassoContext.cachingFs.existsSync(lassoJsonPath)) { // TODO Show a deprecation warning here dependencies.push({ type: 'package', path: lassoJsonPath }); } } // Include all additional dependencies (these were the ones found in the source code) if (requires && requires.length) { requires.forEach(function(inspectResultRequire) { const inspectResultResolved = inspectResultRequire.resolved; const meta = inspectResultResolved.meta; if (meta) { handleMeta(meta, deduper); } const path = inspectResultRequire.path; const requireKey = deduper.requireKey(path, dirname); if (!deduper.hasRequire(requireKey)) { deduper.addDependency(requireKey, { type: 'require', path: inspectResultRequire.path, from: dirname, resolved: inspectResultResolved }); } }); } const defKey = deduper.defKey(resolved.clientPath); if (!deduper.hasDef(defKey)) { const defDependency = { type: 'commonjs-def', path: resolved.clientPath, file: resolved.path }; if (requireHandler.getDefaultBundleName) { // A `virtualModule` object provided a // `getDefaultBundleName` that we will use to // create the define dependency. defDependency.getDefaultBundleName = requireHandler.getDefaultBundleName.bind(requireHandler); } if (additionalVars) { defDependency._additionalVars = additionalVars; } if (requireHandler.object) { // If true, then the module will not be wrapped inside a factory function defDependency.object = true; } if (globalVars) { defDependency.globals = globalVars; } // Pass along the createReadStream and the lastModified to the def dependency defDependency.requireCreateReadStream = inspectResult.createReadStream; defDependency.inspected = inspectResult; defDependency.asyncBlocks = asyncBlocks; defDependency.requireLastModified = inspectResult.lastModified; deduper.addDependency(defKey, defDependency); } // Do we also need to add dependency to run the dependency? if (run === true) { const runKey = deduper.runKey(resolved.clientPath, wait); if (!deduper.hasRun(runKey)) { const runDependency = { type: 'commonjs-run', path: resolved.clientPath, wait, file: resolved.path }; if (requireHandler.getDefaultBundleName) { // A `virtualModule` object provided a // `getDefaultBundleName` that we will use to // create the run dependency. runDependency.getDefaultBundleName = requireHandler.getDefaultBundleName.bind(requireHandler); } if (wait === false) { runDependency.wait = false; } deduper.addDependency(runKey, runDependency); } } if (asyncMeta) { return { dependencies, async: asyncMeta, dirname: nodePath.dirname(resolved.path), filename: resolved.path }; } else { return dependencies; } }); } }; } exports.create = create; ================================================ FILE: src/require/dep-runtime.js ================================================ const nodePath = require('path'); const fs = require('fs'); const lassoModulesClientMainPath = require.resolve('lasso-modules-client'); const modGlobalVarRegex = /\$_mod/g; exports.create = function(config, lasso) { const modulesRuntimeGlobal = config.modulesRuntimeGlobal; return { getDir: function() { return nodePath.dirname(lassoModulesClientMainPath); }, async read (lassoContext) { let contents = await fs.promises.readFile(lassoModulesClientMainPath, 'utf8'); if (modulesRuntimeGlobal) { contents = contents.replace(modGlobalVarRegex, modulesRuntimeGlobal); } return contents; }, getUnbundledTargetPrefix: function(lassoContext) { return config.unbundledTargetPrefix; }, getSourceFile: function() { return lassoModulesClientMainPath; }, calculateKey () { return 'modules-runtime'; } }; }; ================================================ FILE: src/require/dep-transport-builtin.js ================================================ const transport = require('lasso-modules-client/transport'); const nodePath = require('path'); exports.create = function(config, lasso) { return { properties: { name: 'string', target: 'string' }, async init(lassoContext) {}, getDir: function() { return nodePath.dirname(this._sourceFile); }, read: function(context) { return transport.codeGenerators.builtin( this.name, this.target, { modulesRuntimeGlobal: config.modulesRuntimeGlobal }); }, async getLastModified (lassoContext) { return -1; }, getUnbundledTargetPrefix: function(lassoContext) { return config.unbundledTargetPrefix; }, getUnbundledTarget: function() { return 'lasso-modules-meta'; }, getSourceFile: function() { return this._sourceFile; }, calculateKey () { return 'modules-builtin:' + this.name + '>' + this.target; } }; }; ================================================ FILE: src/require/dep-transport-define.js ================================================ const ok = require('assert').ok; const nodePath = require('path'); const streamToString = require('./util/streamToString'); const transport = require('lasso-modules-client/transport'); const StringTransformer = require('./util/StringTransformer'); function shouldPreresolvePath(path) { return true; // if (path.indexOf('$') !== -1) { // // If the require path has a special "$" character then // // we must replace with the preresolved path to avoid // // problems. // return true; // } // // if (path.indexOf('node_modules') !== -1) { // // If the require path includes a "node_modules" directory in the path then // // preresolve since we normalize "node_modules" to "$" // return true; // } // // // if (path.charAt(0) === '.') { // return false; // } // // return true; } function preresolvePath(require, stringTransformer) { if (!require.argRange) { // Can't preresolve the require if we don't know where it is in the string... return; } const resolved = require.resolved; if (shouldPreresolvePath(require.path)) { stringTransformer.comment(require.argRange); stringTransformer.insert(require.argRange[0], '\'' + resolved.clientPath + '\''); } } function transformRequires(code, inspected, asyncBlocks, lassoContext) { // We have two goals with this function: // 1) Comment out all non-JavaScript module requires // require('./test.css'); --> /*require('./test.css');*/ // 2) Update the first argument for all require('lasso-loader').async(...) calls // // In addition, we want to *maintain line numbers* for the transformed code to be nice! const stringTransformer = new StringTransformer(); function transformRequire(require) { ok(require.resolved, '"require.resolved" expected'); if (require.resolved.voidRemap) { if (require.range) { stringTransformer.comment(require.range); stringTransformer.insert(require.range[0], '({})'); } return; } const resolved = require.resolved; if (!resolved.isDir && (resolved.type || !lassoContext.dependencyRegistry.getRequireHandler(resolved.path, lassoContext))) { if (require.range) { stringTransformer.comment(require.range); stringTransformer.insert(require.range[0], 'void 0'); } } else { preresolvePath(require, stringTransformer); } } function transformAsyncCall(asyncBlock) { const name = asyncBlock.name; const firstArgRange = asyncBlock.firstArgRange; if (asyncBlock.packageIdProvided) { // If `name` is not provided then it means that there was no // function body so there is no auto-generated async meta name. if (name) { const packageIdExpression = code.substring(asyncBlock.firstArgRange[0], asyncBlock.firstArgRange[1]); // This path is taken when when async is called no arguments: // For example: // require('lasso-loader').async(blah, function() {...}); stringTransformer.comment(firstArgRange); stringTransformer.insert(firstArgRange[0], '[' + JSON.stringify(name) + ',' + packageIdExpression + ']'); } } else if (asyncBlock.hasInlineDependencies) { ok(name, '"asyncBlock.name" required'); // This path is taken when when async is called with // array of dependencies. // For example: // require('lasso-loader').async(['./a.js', './b.js'], function() {...}); stringTransformer.comment(firstArgRange); stringTransformer.insert(firstArgRange[0], JSON.stringify(name)); } else { ok(name, '"asyncBlock.name" required'); // This path is taken when when async is called no arguments: // For example: // require('lasso-loader').async(function() {...}); stringTransformer.insert(firstArgRange[0], JSON.stringify(name) + ', '); } } if (asyncBlocks && asyncBlocks.length) { asyncBlocks.forEach(transformAsyncCall); } inspected.allRequires.forEach(transformRequire); return stringTransformer.transform(code); } exports.create = function(config, lasso) { return { properties: { path: 'string', file: 'file', globals: 'string', wait: 'boolean', object: 'boolean', inspected: 'object', requireCreateReadStream: 'function', requireLastModified: 'function', asyncBlocks: 'array' }, getDir: function() { return nodePath.dirname(this.file); }, getSourceFile: function() { return this.file; }, read: function(lassoContext) { const requireCreateReadStream = this.requireCreateReadStream; const requireInspected = this.inspected; const asyncBlocks = this.asyncBlocks; const isObject = this.object; const globals = this.globals; const path = this.path; const additionalVars = this._additionalVars; ok(requireCreateReadStream, '"requireCreateReadStream" is required'); ok(requireInspected, '"requireInspected" is required'); ok(path, '"path" is required'); const stream = requireCreateReadStream(); return streamToString(stream) .then((code) => { if (isObject) { return transport.codeGenerators.define(path, code, { object: true, modulesRuntimeGlobal: config.modulesRuntimeGlobal }); } else { const transformedCode = transformRequires(code, requireInspected, asyncBlocks, lassoContext); const defCode = transport.codeGenerators.define( path, transformedCode, { additionalVars, globals, modulesRuntimeGlobal: config.modulesRuntimeGlobal }); return defCode; } }); }, async getLastModified (lassoContext) { return this.requireLastModified; }, getUnbundledTargetPrefix: function(lassoContext) { return config.unbundledTargetPrefix; }, getUnbundledTarget(lassoContext) { return this.path; }, calculateKey () { return 'modules-define:' + this.path; }, toString() { return `[commonjs-def: path="${this.path}"]`; } }; }; ================================================ FILE: src/require/dep-transport-installed.js ================================================ const transport = require('lasso-modules-client/transport'); exports.create = function(config, lasso) { return { properties: { parentPath: 'string', childName: 'string', childVersion: 'string', parentDir: 'string' }, async init(lassoContext) {}, getDir: function() { return this.parentDir; }, read: function(context) { return transport.codeGenerators.installed( this.parentPath, this.childName, this.childVersion, { modulesRuntimeGlobal: config.modulesRuntimeGlobal }); }, async getLastModified (lassoContext) { return -1; }, getUnbundledTargetPrefix: function(lassoContext) { return config.unbundledTargetPrefix; }, getUnbundledTarget: function() { return 'lasso-modules-meta'; }, getSourceFile: function() { return this._sourceFile; }, calculateKey () { return 'modules-installed:' + this.parentPath + '|' + this.childName + '|' + this.childVersion; } }; }; ================================================ FILE: src/require/dep-transport-loader-metadata.js ================================================ const transport = require('lasso-modules-client/transport'); exports.create = function(config, lasso) { return { properties: {}, async init(lassoContext) {}, read: function(lassoContext) { const loaderMetadata = lassoContext && lassoContext.loaderMetadata; if (!loaderMetadata) { return null; } return transport.codeGenerators.loaderMetadata( loaderMetadata, lassoContext, { modulesRuntimeGlobal: config.modulesRuntimeGlobal }); }, getUnbundledTargetPrefix: function(lassoContext) { return config.unbundledTargetPrefix; }, getUnbundledTarget: function() { return 'lasso-modules-meta'; }, calculateKey () { return 'loader-metadata'; }, isPageBundleOnlyDependency: function() { return true; }, async getLastModified (lassoContext) { return -1; } }; }; ================================================ FILE: src/require/dep-transport-main.js ================================================ const transport = require('lasso-modules-client/transport'); const nodePath = require('path'); exports.create = function(config, lasso) { return { properties: { dir: 'string', main: 'string' }, async init() {}, getDir: function() { return nodePath.dirname(this._sourceFile); }, read: function(context) { return transport.codeGenerators.main( this.dir, this.main, { modulesRuntimeGlobal: config.modulesRuntimeGlobal }); }, async getLastModified (lassoContext) { return -1; }, getSourceFile: function() { return this._sourceFile; }, calculateKey () { return 'modules-main:' + this.dir + '|' + this.main; }, getUnbundledTargetPrefix: function(lassoContext) { return config.unbundledTargetPrefix; }, getUnbundledTarget: function() { return 'lasso-modules-meta'; } }; }; ================================================ FILE: src/require/dep-transport-ready.js ================================================ const transport = require('lasso-modules-client/transport'); exports.create = function(config, lasso) { return { properties: { }, async init() { if (!this.slot) { delete this.slot; } }, getDir: function() { return null; }, read: function(context) { return transport.codeGenerators.ready({ modulesRuntimeGlobal: config.modulesRuntimeGlobal }); }, calculateKey () { return 'modules-ready'; }, async getLastModified (lassoContext) { return -1; } }; }; ================================================ FILE: src/require/dep-transport-remap.js ================================================ const transport = require('lasso-modules-client/transport'); const nodePath = require('path'); exports.create = function(config, lasso) { return { properties: { from: 'string', to: 'string', fromFile: 'string' }, async init (lassoContext) {}, getDir: function() { return this.fromFile ? nodePath.dirname(this.fromFile) : undefined; }, read: function(context) { return transport.codeGenerators.remap( this.from, this.to, { modulesRuntimeGlobal: config.modulesRuntimeGlobal }); }, async getLastModified (lassoContext) { return -1; }, getSourceFile: function() { return this._sourceFile; }, calculateKey () { return 'modules-remap:' + this.from + '|' + this.to; }, getUnbundledTargetPrefix: function(lassoContext) { return config.unbundledTargetPrefix; }, getUnbundledTarget: function() { return 'lasso-modules-meta'; } }; }; ================================================ FILE: src/require/dep-transport-run.js ================================================ const nodePath = require('path'); const transport = require('lasso-modules-client/transport'); exports.create = function(config, lasso) { return { properties: { path: 'string', wait: 'boolean', file: 'string' // The original source file that this dependency is assocaited with }, async init(lassoContext) {}, getDir: function() { return this.path ? nodePath.dirname(this.path) : undefined; }, read: function(lassoContext) { // the default is to wait so only output options // if the wait value is not equal to the default value const runOptions = (this.wait === false) ? { wait: false } : undefined; return transport.codeGenerators.run( // the path to the resource this.path, // options for runCode runOptions, // options that affect how the code is generated { modulesRuntimeGlobal: config.modulesRuntimeGlobal }); }, async getLastModified (lassoContext) { return -1; }, getUnbundledTargetPrefix: function(lassoContext) { return config.unbundledTargetPrefix; }, getUnbundledTarget: function() { let bundleName = this.path; const ext = nodePath.extname(bundleName); if (ext) { bundleName = bundleName.substring(0, bundleName.length - ext.length); } return bundleName + '-run' + ext; }, calculateKey () { return 'modules-run:' + this.path + '|' + this.wait; } }; }; ================================================ FILE: src/require/dep-transport-search-path.js ================================================ const transport = require('lasso-modules-client/transport'); exports.create = function(config, lasso) { return { properties: { path: 'string', paths: 'string[]' }, async init () { if (!this.paths) { this.paths = []; } if (this.path) { this.paths.push(this.path); } }, getDir: function() { return null; }, read: function(context) { return transport.codeGenerators.searchPath(this.paths, { modulesRuntimeGlobal: config.modulesRuntimeGlobal }); }, async getLastModified (lassoContext) { return -1; }, getUnbundledTargetPrefix: function(lassoContext) { return config.unbundledTargetPrefix; }, getUnbundledTarget: function() { return 'lasso-modules-meta'; }, calculateKey () { return 'modules-search-path:' + JSON.stringify(this.paths); } }; }; ================================================ FILE: src/require/index.js ================================================ const fs = require('fs'); const depRequire = require('./dep-require'); const depRequireRemap = require('./dep-require-remap'); const depTransportDef = require('./dep-transport-define'); const depTransportRun = require('./dep-transport-run'); const depTransportInstalled = require('./dep-transport-installed'); const depTransportMain = require('./dep-transport-main'); const depTransportRemap = require('./dep-transport-remap'); const depTransportReady = require('./dep-transport-ready'); const depTransportBuiltin = require('./dep-transport-builtin'); const depTransportSearchPath = require('./dep-transport-search-path'); const depLoaderMetadata = require('./dep-transport-loader-metadata'); const depRuntime = require('./dep-runtime'); const buildPluginConfig = require('./build-plugin-config'); const extend = require('raptor-util').extend; const requireRegExp = /^require\s+(.*)$/; const requireRunRegExp = /^require-run\s*:\s*(.*)$/; module.exports = exports = function plugin(lasso, userConfig) { const defaultProjectRoot = lasso.config.getProjectRoot(); const config = buildPluginConfig(userConfig, defaultProjectRoot); lasso.on('lassoCacheCreated', function(cacheInfo) { const lassoCache = cacheInfo.lassoCache; lassoCache.configureCacheDefaults({ '*': { // Any profile 'lasso-require/inspect': { store: 'disk', encoding: 'utf8', valueType: 'json' }, 'lasso-require/transformed': { store: 'disk', singleFile: false, encoding: 'utf8' } } }); }); function registerExtension(ext) { lasso.dependencies.registerRequireExtension(ext, { read: function(path) { return fs.createReadStream(path, { encoding: 'utf8' }); }, async getLastModified (path, lassoContext) { return lassoContext.getFileLastModified(path); } }); } config.extensions.forEach(registerExtension); lasso.dependencies.registerRequireExtension('json', { object: true, read: function(path) { return fs.createReadStream(path, { encoding: 'utf8' }); }, getLastModified (path, lassoContext) { return lassoContext.getFileLastModified(path); } }); lasso.dependencies.registerJavaScriptType('commonjs-def', depTransportDef.create(config, lasso)); lasso.dependencies.registerJavaScriptType('commonjs-run', depTransportRun.create(config, lasso)); lasso.dependencies.registerJavaScriptType('commonjs-installed', depTransportInstalled.create(config, lasso)); lasso.dependencies.registerJavaScriptType('commonjs-main', depTransportMain.create(config, lasso)); lasso.dependencies.registerJavaScriptType('commonjs-remap', depTransportRemap.create(config, lasso)); lasso.dependencies.registerJavaScriptType('commonjs-ready', depTransportReady.create(config, lasso)); lasso.dependencies.registerJavaScriptType('commonjs-search-path', depTransportSearchPath.create(config, lasso)); lasso.dependencies.registerJavaScriptType('commonjs-runtime', depRuntime.create(config, lasso)); lasso.dependencies.registerJavaScriptType('commonjs-builtin', depTransportBuiltin.create(config, lasso)); lasso.dependencies.registerJavaScriptType('loader-metadata', depLoaderMetadata.create(config, lasso)); lasso.dependencies.registerPackageType('require', depRequire.create(config, lasso)); lasso.dependencies.registerPackageType('require-remap', depRequireRemap.create(config, lasso)); lasso.dependencies.addNormalizer(function(dependency) { if (typeof dependency === 'string') { let matches; if ((matches = requireRegExp.exec(dependency))) { return { type: 'require', path: matches[1] }; } else if ((matches = requireRunRegExp.exec(dependency))) { return { type: 'require', path: matches[1], run: true }; } } else if (!dependency.type) { if (dependency.require) { const reqDep = { type: 'require', path: dependency.require }; extend(reqDep, dependency); delete reqDep.require; return reqDep; } else if (dependency['require-run']) { const reqRunDep = { type: 'require', run: true, path: dependency['require-run'] }; extend(reqRunDep, dependency); delete reqRunDep['require-run']; return reqRunDep; } } }); }; // module.exports.getClientPath = require('lasso-modules-client/transport').getClientPath; ================================================ FILE: src/require/inspect-cache.js ================================================ const ok = require('assert').ok; const logger = require('raptor-logging').logger(module); const streamToString = require('./util/streamToString'); const inspect = require('./util/inspect'); const nodePath = require('path'); const extend = require('raptor-util/extend'); const normalizeFSPath = require('./util/normalizeFSPath'); exports.inspectCached = function(path, requireHandler, lassoContext, config) { const debugEnabled = logger.isDebugEnabled(); ok(path, '"path" is required'); ok(requireHandler, '"requireHandler" is required'); ok(lassoContext, '"lassoContext" is required'); ok(config, '"config" is required'); ok(typeof path === 'string', '"path" should be a string'); ok(typeof requireHandler.createReadStream === 'function', '"requireHandler.createReadStream" should be a function'); ok(typeof requireHandler.getLastModified === 'function', '"requireHandler.getLastModified" should be a function'); ok(typeof lassoContext === 'object', '"lassoContext" should be an object'); ok(typeof config === 'object', '"config" should be an object'); function resolveInspectedRequires(inspectResult) { const allRequires = []; const fromDir = nodePath.dirname(path); function handleRequire(require) { const resolved = lassoContext.resolveCached(require.path, fromDir); const pathRelative = nodePath.relative(process.cwd(), path); const fromRelative = nodePath.relative(process.cwd(), fromDir); if (!resolved) { throw new Error('Module not found: ' + require.path + ' (from "' + fromRelative + '" and referenced in "' + pathRelative + '")'); } // Clone the require require = extend({}, require); require.resolved = resolved; allRequires.push(require); return require; } if (inspectResult.requires) { inspectResult.requires = inspectResult.requires.map(handleRequire); } if (inspectResult.asyncBlocks) { inspectResult.asyncBlocks.forEach(function(asyncBlock) { asyncBlock.requires = asyncBlock.requires.map(handleRequire); }); } inspectResult.allRequires = allRequires; } // Get or create the required caches const transformsId = config.transforms ? '/' + config.transforms.id : ''; let inspectCache = lassoContext.data['lasso-require/inspect']; if (!inspectCache && lassoContext.cache) { inspectCache = lassoContext.data['lasso-require/inspect'] = lassoContext.cache.getCache( // Unique cache name based on the set of enabled require transforms: 'lasso-require/inspect' + (transformsId ? '-' + transformsId : ''), // NOTE: ".1" is just needed for cache busting old versions // Name of the cache configuration to use: 'lasso-require/inspect'); } let src; let lastModified; function readSource() { if (src != null) { // We have already read in the source code for the require so just return that! return Promise.resolve(src); } // Otherwise, let's read in the stream into a string value and invoke the callback when it is done. const stream = requireHandler.createReadStream(); return streamToString(stream) .then((_src) => { src = _src; return src; }); } let fromCache = true; function cacheBuilder () { fromCache = false; return readSource() .then((src) => { return inspect(src, { filename: path }); }); } function afterInspect (inspectResult) { if (debugEnabled) { logger.debug('Inspection result for ' + path + ': ' + JSON.stringify(inspectResult)); } // Do a shallow clonse so that we don't modify the object stored in the cache inspectResult = extend({}, inspectResult); inspectResult.lastModified = lastModified || -1; if (fromCache) { inspectResult.fromCache = fromCache; } resolveInspectedRequires(inspectResult); if (src) { // If src is non-null then that means that the builder needed to be invoked to read // the require dependency to inspect the source. Since we had to read the dependency let's // also provide the src so that we don't need to re-read it to generate the final // output bundle inspectResult.createReadStream = function() { return lassoContext.deferredStream(function() { this.push(src); this.push(null); }); }; } else { inspectResult.createReadStream = requireHandler.createReadStream.bind(requireHandler); } return inspectResult; } // Inspecting a JavaScript file is expensive since it requires parsing the JavaScript to find all of the // requires. We really don't want to do that every time so we *always* calculate a cache key for the // the dependency. In the normal case we use the "lastModiifed" time for the require, but in case where // that is not available then we read in the JavaScript code for the require and calculate a fingerprint // on the provided source and use that as a cache key. /** * This method does the final inspection after we have calculated the cache key. * At this point we may or may not have actually read in the source for the require. * @return {[type]} [description] */ function doInspect(cacheKey) { ok(cacheKey); if (!inspectCache) { return cacheBuilder().then(afterInspect); } // try to read the inspect result from the cache return inspectCache.get( cacheKey, { lastModified: lastModified && lastModified > 0 ? lastModified : undefined, builder: cacheBuilder }) .then(afterInspect); } function buildCacheKeyFromFingerprint() { return new Promise((resolve, reject) => { // We are going to need to read in the source code for the require to calculate the fingerprint. // We will use the fingerprint as a cache key to avoid having to inspect the JavaScript in the // case where there is cache hit. Since we have already read in the source this won't need to be // done later in the pipeline. src = ''; let fingerprint = null; const stream = requireHandler.createReadStream(); const fingerprintStream = lassoContext.createFingerprintStream(); fingerprintStream .on('fingerprint', function(_fingerprint) { fingerprint = _fingerprint; }) .on('data', function(data) { src += data; }) .on('end', function() { resolve(fingerprint); }) .on('error', reject); stream .on('error', reject) .pipe(fingerprintStream); }); } return requireHandler.getLastModified() .then((_lastModified) => { lastModified = _lastModified; if (!lastModified || lastModified < 0) { lastModified = undefined; } if (lastModified) { return normalizeFSPath(path); } else { return buildCacheKeyFromFingerprint(); } }) .then(doInspect); }; ================================================ FILE: src/require/util/Deduper.js ================================================ const DeduperContext = require('./DeduperContext'); const ok = require('assert').ok; const REQUIRE_DEDUPER_CONTEXT_KEY = 'dependency-require'; const hasOwn = Object.prototype.hasOwnProperty; class Deduper { constructor(lassoContext, dependencies) { ok(lassoContext, '"lassoContext" is required'); ok(dependencies, '"dependencies" is required'); /* * NOTE: The use of "phaseData" was necessary because we want to keep a cache that is independent of * for each phase of the optimization process. The optimization is separated into phases such as "app-bundle-mappings", * "page-bundle-mappings", "async-page-bundle-mappings", etc. We use the "deduperContext" to prevent adding the same * require dependencies over and over again. */ const deduperContext = lassoContext.phaseData[REQUIRE_DEDUPER_CONTEXT_KEY] || (lassoContext.phaseData[REQUIRE_DEDUPER_CONTEXT_KEY] = new DeduperContext()); this.dependencies = dependencies; this.deduperContext = deduperContext; const lookups = deduperContext.lookups; this.lookupDef = lookups.def; this.lookupRun = lookups.run; this.lookupInstalled = lookups.installed; this.lookupMain = lookups.main; this.lookupRemap = lookups.remap; this.lookupRequire = lookups.require; this.lookupBuiltin = lookups.builtin; this.lookupSearchPath = lookups.searchPath; } addDependency(key, d) { this.lookupDef[key] = true; this.dependencies.push(d); } // Define defKey(path) { return path; } hasDef(key) { return hasOwn.call(this.lookupDef, key); } // Run runKey(path, wait) { return wait ? path : path + '|nowait'; } hasRun(key) { return hasOwn.call(this.lookupRun, key); } // Installed installedKey(parentPath, childName, childVersion) { return parentPath + '|' + childName + '|' + childVersion; } hasInstalled(key) { return hasOwn.call(this.lookupInstalled, key); } // Main mainKey(dir, main) { return dir + '|' + main; } hasMain(key) { return hasOwn.call(this.lookupMain, key); } // Remap remapKey(from, to) { return from + '|' + to; } hasRemap(key) { return hasOwn.call(this.lookupRemap, key); } // Require requireKey(path, from, run, wait) { let key = path + '@' + from; if (run) { key += '|run|' + wait; } return key; } hasRequire(key) { return hasOwn.call(this.lookupRequire, key); } // Builtin builtinKey(name, target) { return name + '>' + target; } hasBuiltin(key) { return hasOwn.call(this.lookupBuiltin, key); } // Search path searchPathKey(path) { return path; } hasSearchPath(key) { return hasOwn.call(this.lookupSearchPath, key); } addRuntime(runtimeDependency) { if (this.deduperContext.runtimeInclude === false) { this.dependencies.push(runtimeDependency); this.deduperContext.runtimeInclude = true; } } addReady(readyDependency) { if (this.deduperContext.readyIncluded === false) { // Add a dependency that will trigger all of the deferred // run modules to run once all of the code has been loaded // for the page this.dependencies.push(readyDependency); this.deduperContext.readyIncluded = true; } } addProcess(d) { if (this.deduperContext.processIncluded === false) { this.dependencies.push(d); this.deduperContext.processIncluded = true; } } } module.exports = Deduper; ================================================ FILE: src/require/util/DeduperContext.js ================================================ class DeduperContext { constructor() { this.lookups = { def: {}, run: {}, installed: {}, main: {}, remap: {}, require: {}, builtin: {}, searchPath: {} }; this.runtimeInclude = false; this.readyIncluded = false; this.processIncluded = false; } } module.exports = DeduperContext; ================================================ FILE: src/require/util/StringTransformer.js ================================================ function StringTransformer() { this.modifications = []; } StringTransformer.prototype = { transform: function(str) { if (this.modifications.length === 0) { return str; } this.modifications.sort(function(a, b) { const compare = b.index - a.index; if (compare === 0) { return a.precedence - b.precedence; } else { return compare; } }); for (let i = 0, len = this.modifications.length; i < len; i++) { str = this.modifications[i].transform(str); } return str; }, insert: function(index, newStr) { this.modifications.push({ index, precedence: 3, toString: function() { return 'insert ' + index + ' --> ' + newStr; }, transform: function(str) { return str.substring(0, index) + newStr + str.substring(index); } }); }, replace: function(range, replacement) { this.modifications.push({ index: range[0], precedence: 2, toString: function() { return 'replace ' + range + ' --> ' + replacement; }, transform: function(str) { return str.substring(0, range[0]) + replacement + str.substring(range[1]); } }); }, comment: function(range) { this.modifications.push({ index: range[0], precedence: 1, toString: function() { return 'comment ' + range; }, transform: function(str) { const code = str.substring(range[0], range[1]); return str.substring(0, range[0]) + '/*' + code + '*/' + str.substring(range[1]); } }); } }; module.exports = StringTransformer; ================================================ FILE: src/require/util/Transforms.js ================================================ const logger = require('raptor-logging').logger(module); const crypto = require('crypto'); const ok = require('assert').ok; const PassThrough = require('stream').PassThrough; const inspect = require('util').inspect; const util = require('util'); const stream = require('stream'); const resolveFrom = require('resolve-from'); class TransformAdaptorStream extends stream.Transform { constructor(transform, lassoContext) { super(); this.data = ''; this.transformFunc = transform.func; this.lassoContext = lassoContext; this.transformName = transform.name; } _transform(buf, enc, callback) { // Collect all of the data as it is streamed in and just concatenate to a our data string // but don't actually stream out any data yet this.data += buf; callback(); } _flush(callback) { // On the last flush we apply the transform by calling the transform function on the // data string that was collected from all input chunks const transformFunc = this.transformFunc; let result = transformFunc(this.data, this.lassoContext); if (result == null) { result = ''; } if (typeof result === 'string') { // Did the transform synchronously return some data? this.push(result); callback(); } else if (typeof result.then === 'function') { // Did the transform return a promise // The transform appears to have returned a Promise result .then((code) => { this.push(code); callback(); }) .catch((err) => { try { this.emit('error', err); } finally { callback(); } }); } else { // Invalid transform... try { this.emit('error', new Error(`The ${this.transformName} did not return a valid value`)); } finally { callback(); } } } } function resolvePath(path, projectRoot) { let resolvedPath; if (projectRoot) { resolvedPath = resolveFrom(projectRoot, path); if (resolvedPath) { return resolvedPath; } } else { resolvedPath = resolveFrom(process.cwd(), path); if (resolvedPath) { return resolvedPath; } } return require.resolve(path); } class Transforms { constructor(transforms, projectRoot) { this._transforms = new Array(transforms.length); const shasum = crypto.createHash('sha1'); transforms.forEach((curTransform, i) => { if (!curTransform) { throw new Error('Invalid require transform at index ' + i); } let transformFunc; let transformId; let stream = true; let transformName; if (typeof curTransform === 'string') { curTransform = { transform: curTransform }; } else if (typeof curTransform === 'function') { curTransform = { transform: curTransform }; } if (typeof curTransform === 'object') { let transform = curTransform.transform; if (transform) { if (typeof transform === 'string') { const transformPath = resolvePath(transform, projectRoot); transform = require(transformPath); transformId = transform.id || transformPath; } } else { transform = curTransform; } const transformConfig = curTransform.config; if (typeof transform === 'function') { stream = true; // Looks like a browserify style-transform transformFunc = function(path, lassoContext) { return transform(path, transformConfig); }; transformId = transformFunc.toString(); } else if (transform.createTransform) { stream = transform.stream !== false; transformName = transform.name; transformFunc = transform.createTransform(transformConfig || {}); transformId = transform.id || transform.createTransform.toString(); } else { throw new Error('Invalid require transform at index ' + i + ': ' + util.inspect(curTransform)); } } else { throw new Error('Invalid require transform at index ' + i + ': ' + util.inspect(curTransform)); } if (!transformId) { transformId = transformFunc.toString(); } ok(typeof transformFunc === 'function', 'Invalid transform at index ' + i); this._transforms[i] = { func: transformFunc, id: transformId, name: transformName || transformFunc.name, stream }; shasum.update(transformId); }); this.id = shasum.digest('hex'); } apply(path, inStream, lassoContext) { ok(inStream, 'inStream is required'); const transforms = this._transforms; lassoContext = Object.create(lassoContext); lassoContext.filename = path; const len = transforms.length; if (!len) { // If there are no transforms then just return the input stream return inStream; } function applyTransform(input, transform) { if (transform.stream === false) { // The TransformAdaptorStream class extends stream.Transform and it is used // to convert a synchronous or Promise transform to a stream-based transform return input.pipe(new TransformAdaptorStream(transform, lassoContext)); } else { return input.pipe(transform.func(path, lassoContext)); } } return lassoContext.deferredStream(function() { const deferredStream = this; let finished = false; function handleError(e) { if (finished) { return; } finished = true; deferredStream.emit('error', e); deferredStream.push(null); // End the stream just in case } inStream.on('error', handleError); const passThrough = new PassThrough({ encoding: 'utf8' }); let out = passThrough; for (let i = 0, len = transforms.length; i < len; i++) { const curTransform = transforms[i]; const transformName = curTransform.name; if (logger.isDebugEnabled()) { logger.debug('Applying transform ' + transformName); } // applyTransform will return a new stream that we can read from out = applyTransform(out, curTransform); if (typeof out.pipe !== 'function') { return handleError(new Error('Non-stream object returned from transform (transform=' + transformName + ', output=' + inspect(out) + ')')); } out.on('error', handleError); } // Now start the flow of data at the source by piping the input stream // to the beginning of our transform chain (i.e. the initial pass thorugh stream) inStream.pipe(passThrough); return out; }); } } module.exports = Transforms; ================================================ FILE: src/require/util/inspect.js ================================================ const path = require('path'); const espree = require('espree'); const codeFrame = require('@babel/code-frame').default; const estraverse = require('estraverse'); const ok = require('assert').ok; const cwd = process.cwd(); const parseOpts = { range: true, sourceType: 'script', ecmaVersion: espree.latestEcmaVersion }; const shortCircuitRegExp = /require\(|require\.resolve\(|.async\(|#async|process|Buffer/; function isRequire(node) { return node.type === 'CallExpression' && node.callee.type === 'Identifier' && node.callee.name === 'require' && node.arguments.length === 1 && node.arguments[0].type === 'Literal' && typeof node.arguments[0].value === 'string'; } function isRequireResolve(node) { return node.type === 'CallExpression' && node.callee.type === 'MemberExpression' && node.callee.object.type === 'Identifier' && node.callee.object.name === 'require' && node.callee.property.type === 'Identifier' && node.callee.property.name === 'resolve' && node.arguments.length === 1 && node.arguments[0].type === 'Literal'; } function isRequireFor(node, moduleName) { return node.type === 'CallExpression' && node.callee.type === 'Identifier' && node.callee.name === 'require' && node.arguments.length === 1 && node.arguments[0].type === 'Literal' && node.arguments[0].value === moduleName; } function isRequireForAsyncLoader(node) { return isRequireFor(node, 'lasso-loader') || isRequireFor(node, 'raptor-loader'); } function isAsyncNode(node, scope) { if (!node.arguments || !node.arguments.length) { return false; } if (node.type !== 'CallExpression' || node.callee.type !== 'MemberExpression' || node.callee.property.type !== 'Identifier' || node.callee.property.name !== 'async') { return false; } if (isRequireForAsyncLoader(node.callee.object)) { return true; } if (node.callee.object.type === 'Identifier' && (scope[node.callee.object.name] === 'lasso-loader')) { return true; } return false; } function parseAsyncNode(node, scope) { if (!isAsyncNode(node, scope)) { return; } const args = node.arguments; const numArguments = args.length; if ((numArguments < 1) || (numArguments > 2)) { return; } const dependencies = []; let hasInlineDependencies = false; let packageIdProvided; const firstArg = args[0]; // We only care if about the async calls if the first argument is an array if (numArguments === 2) { if (firstArg.type === 'ArrayExpression') { hasInlineDependencies = true; // call is something like: // require('lasso-loader').async(['./dep1.js', './dep2.js'], callback) const elems = firstArg.elements; for (let i = 0; i < elems.length; i++) { dependencies.push(elems[i].value); } } else { // call is something like: // require('lasso-loader').async('somePackageId', callback) // require('lasso-loader').async(someVariable, callback) packageIdProvided = true; } } const callbackNode = args[numArguments - 1]; const hasFunctionBody = (callbackNode.type === 'FunctionExpression') || (callbackNode.type === 'FunctionDeclaration'); return { node, requires: [], dependencies, args, callbackNode, // require('lasso-loader').async(packageId, function() {}) is used // then `packageIdProvided` will be `true` packageIdProvided, // Store the range of the first arg in case we need to replace // or add to it. firstArgRange: args[0].range, // If the first argument to require('lasso-loader').async([...], callback) is // is used then `hasInlineDependencies` will be `true` hasInlineDependencies, // If the last argument to require('lasso-loader').async(...) // is a `FunctionDeclaration` or `FunctionExpression` then // `hasFunctionBody` will be `true`. hasFunctionBody, toString: function() { return '[async: ' + this.name + ', dependencies=' + JSON.stringify(dependencies) + ']'; } }; } function recordGlobalsHelper(node, scope, foundGlobals) { if (!node || node.type !== 'Identifier') { return; } const id = node.name; if (id === 'require' || id === 'exports' || id === 'module' || id === 'arguments' || id === '__dirname' || id === '__filename') { // We don't require about these "globals" return; } if (!scope[id]) { foundGlobals[id] = true; } } function recordGlobals(node, parentNode, scope, foundGlobals) { if (node.type === 'Identifier') { if (parentNode.type === 'MemberExpression' || parentNode.type === 'Property' || parentNode.type === 'VariableDeclarator' || parentNode.type === 'FunctionDeclaration' || parentNode.type === 'FunctionExpression') { return; } recordGlobalsHelper(node, scope, foundGlobals); } else if (node.type === 'MemberExpression') { if (parentNode !== 'MemberExpression') { recordGlobalsHelper(node.object, scope, foundGlobals); } } else if (node.type === 'Property') { recordGlobalsHelper(node.value, scope, foundGlobals); } else if (node.type === 'VariableDeclarator') { recordGlobalsHelper(node.init, scope, foundGlobals); } else if (node.type === 'FunctionDeclaration' || node.type === 'FunctionExpression') { if (node.type === 'FunctionDeclaration' && node.id) { delete foundGlobals[node.id.name]; } // Skip the params and the function ID and just look at the body nodes node.body.body.forEach((bodyNode) => { recordGlobalsHelper(bodyNode, scope, foundGlobals); }); } } module.exports = function inspect(src, options) { ok(src != null, 'src is requried'); const allowShortcircuit = !options || options.allowShortcircuit !== false; if (allowShortcircuit && shortCircuitRegExp.test(src) === false) { // Nothing of interest so nothing to do return { requires: [], foundGlobals: {}, asyncBlocks: [] }; } const requires = []; const scopeStack = [{}]; const asyncScopeStack = []; const asyncStack = []; let curAsyncInfo = null; let asyncBlocks = []; const foundGlobals = {}; let parsedAst; try { parsedAst = espree.parse(src, parseOpts); } catch (err) { if (!err.lineNumber) { throw err; } const filename = options && options.filename; let errorLoc = '(' + err.lineNumber + ',' + err.column + '): '; if (filename) { errorLoc = path.relative(cwd, filename) + errorLoc; } const frame = codeFrame(src, err.lineNumber, err.column, { highlightCode: true }); throw new SyntaxError(errorLoc + err.message + '\n' + frame); } estraverse.traverse(parsedAst, { enter: function(node, parentNode) { let scope = scopeStack[scopeStack.length - 1]; if (node.type === 'VariableDeclaration') { node.declarations.forEach(function(varDecl) { if (varDecl.init && isRequireForAsyncLoader(varDecl.init)) { scope[varDecl.id.name] = 'lasso-loader'; } else { scope[varDecl.id.name] = true; } }); } else if (node.type === 'FunctionExpression' || node.type === 'FunctionDeclaration') { scope = Object.create(scope); node.params.forEach((param) => { scope[param.name] = true; }); if (node.type === 'FunctionDeclaration') { scopeStack[scopeStack.length - 1][node.id.name] = true; } scopeStack.push(scope); } recordGlobals(node, parentNode, scope, foundGlobals); let requirePath; if (!scope.require && (isRequire(node) || isRequireResolve(node))) { requirePath = node.arguments[0].value; const range = node.range; const firstArgRange = node.arguments[0].range; if (asyncScopeStack.length) { // We are in the scope of an async callback function so this // is a dependency that will be lazily loaded if (requirePath !== 'lasso-loader' && requirePath !== 'raptor-loader') { const lastAsyncInfo = asyncScopeStack[asyncScopeStack.length - 1]; lastAsyncInfo.requires.push({ path: requirePath, range, argRange: firstArgRange }); lastAsyncInfo.dependencies.push({ type: 'require', path: requirePath }); } } else { requires.push({ path: requirePath, range, argRange: firstArgRange }); } } let asyncInfo; if ((asyncInfo = parseAsyncNode(node, scopeStack[scopeStack.length - 1]))) { curAsyncInfo = asyncInfo; asyncBlocks.push(asyncInfo); asyncStack.push(asyncInfo); } else if (curAsyncInfo && node === curAsyncInfo.callbackNode) { // We are in the scope of the async callback function so // all dependencies below this will be async asyncScopeStack.push(curAsyncInfo); curAsyncInfo = null; } }, leave: function(node, parentNode) { if (node.type === 'FunctionExpression' || node.type === 'FunctionDeclaration') { scopeStack.pop(); } if (asyncStack.length && node === asyncStack[asyncStack.length - 1].node) { asyncStack.pop(); } else if (asyncScopeStack.length && node === asyncScopeStack[asyncScopeStack.length - 1].callbackNode) { asyncScopeStack.pop(); } } }); asyncBlocks = asyncBlocks.map((asyncBlock) => { delete asyncBlock.node; delete asyncBlock.args; delete asyncBlock.callbackNode; return asyncBlock; }); return { requires, foundGlobals, asyncBlocks }; }; ================================================ FILE: src/require/util/normalizeFSPath.js ================================================ const lassoModulesClientTransport = require('lasso-modules-client/transport'); function normalizeFSPath(path) { return lassoModulesClientTransport.getClientPath(path); } module.exports = normalizeFSPath; ================================================ FILE: src/require/util/streamToString.js ================================================ module.exports = function(stream) { return new Promise((resolve, reject) => { let str = ''; stream .on('data', function(data) { str += data; }) .on('error', function(err) { reject(err); }) .on('end', function() { resolve(str); }); }); }; ================================================ FILE: src/resolve/builtins.js ================================================ const lassoResolveFrom = require('lasso-resolve-from'); const extend = require('raptor-util/extend'); function resolveBuiltin(target) { const resolved = lassoResolveFrom(__dirname, target); if (!resolved) { throw new Error('Missing builtin: ' + target); } return resolved.path; } const defaultBuiltins = { assert: resolveBuiltin('assert'), buffer: resolveBuiltin('buffer'), events: resolveBuiltin('events'), 'lasso-loader': resolveBuiltin('lasso-loader'), path: resolveBuiltin('path-browserify'), process: resolveBuiltin('process'), 'raptor-loader': resolveBuiltin('lasso-loader'), stream: resolveBuiltin('stream-browserify'), string_decoder: resolveBuiltin('string_decoder'), url: resolveBuiltin('url'), util: resolveBuiltin('util') }; exports.getBuiltins = function(additionalBuiltins) { const allBuiltins = extend({}, defaultBuiltins); function addBuiltins(builtins) { Object.keys(builtins).forEach(function(packageName) { const builtinTarget = builtins[packageName]; if (typeof builtinTarget !== 'string') { throw new Error('Invalid builtin: ' + packageName + ' (target: ' + builtinTarget + ')'); } allBuiltins[packageName] = builtinTarget; }); } if (additionalBuiltins) { addBuiltins(additionalBuiltins); } return allBuiltins; }; ================================================ FILE: src/resolve/getRequireRemapFromDir.js ================================================ const nodePath = require('path'); module.exports = function(fromDir, lassoContext) { const lassoJsonPath = nodePath.join(fromDir, 'browser.json'); let remap; if (lassoContext.cachingFs.existsSync(lassoJsonPath)) { const lassoPackage = lassoContext.readPackageFile(lassoJsonPath); remap = lassoPackage.getRequireRemap(lassoContext); } return remap; }; ================================================ FILE: src/resolve/index.js ================================================ const builtinsModule = require('./builtins'); const parseRequire = require('./parseRequire'); const nodePath = require('path'); const getRequireRemapFromDir = require('./getRequireRemapFromDir'); const lassoResolveFrom = require('lasso-resolve-from'); const ok = require('assert').ok; const cachingFs = require('../caching-fs'); const _normalizePath = nodePath.sep === '/' ? function _normalizePathUnix(path) { // nothing to do for non-Windows platform return path; } : function _normalizePathWindows(path) { // replace back-slash with forward-slash return path.replace(/[\\]/g, '/'); }; exports.createResolver = function(lassoContext, getClientPath) { const resolverConfig = lassoContext.config && lassoContext.config.resolver; const requireConfig = lassoContext.config && lassoContext.config._requirePluginConfig; const builtinsConfig = (resolverConfig && resolverConfig.builtins) || (requireConfig && requireConfig.builtins); const postResolveFn = resolverConfig && resolverConfig.postResolve; const builtins = builtinsModule.getBuiltins(builtinsConfig); function resolve(targetModule, fromDir, options) { ok(targetModule, '"targetModule" is required'); ok(typeof targetModule === 'string', '"targetModule" should be a string'); ok(typeof fromDir === 'string', '"fromDir" should be a string'); const parsedRequire = parseRequire(targetModule); // Normalize the path by making sure the path separator is always forward slash // (normalize does nothing on non-Windows platform) targetModule = _normalizePath(parsedRequire.path); const dependencyType = parsedRequire.type; const resolveOptions = { includeMeta: true }; resolveOptions.extensions = lassoContext.dependencyRegistry.getRequireExtensionNames(); resolveOptions.remaps = function(dir) { return lassoContext && getRequireRemapFromDir(dir, lassoContext); }; let resolvedInfo = lassoResolveFrom(fromDir, targetModule, resolveOptions); const isBuiltin = resolvedInfo && builtins && builtins[targetModule] === resolvedInfo.path; if (!(resolvedInfo && !isBuiltin)) { if (targetModule.charAt(0) === '.') { return null; } const resolvedBuiltin = builtins[targetModule]; if (resolvedBuiltin) { resolvedInfo = { path: resolvedBuiltin, meta: [ { type: 'builtin', name: targetModule, target: resolvedBuiltin } ] }; } else if (options && options.moduleFallbackToRelative) { const resolvedPath = nodePath.resolve(fromDir, targetModule); // Since the path looked like it was for a module we should check // to see if the fallback technique actually found a file. If file // does not exist for fallback path, then we'll report an error // that the module does not exist by re-throwing the original error. if (cachingFs.existsSync(resolvedPath)) { // Fallback technique found the path. // We might want to log something here to suggest that relative // paths be prefixed with "." to avoid the extra work of trying to // resolve path using NodeJS module search path. resolvedInfo = { path: resolvedPath }; } } } if (resolvedInfo) { if (postResolveFn) { postResolveFn(resolvedInfo, lassoContext); } const result = { path: resolvedInfo.path, meta: resolvedInfo.meta, clientPath: getClientPath(resolvedInfo.path) }; if (resolvedInfo.voidRemap) { result.voidRemap = true; } if (dependencyType) { result.type = dependencyType; } return result; } else { // Path is not a module or resolved path throw new Error('Failed to resolve "' + targetModule + '". Target file does not exist. Started search from directory "' + fromDir + '".'); } } function resolveCached(targetModule, fromDir, options) { if (!lassoContext.cache) { return resolve(targetModule, fromDir, options); } const key = targetModule + '@' + fromDir; const cache = lassoContext.cache.getSyncCache('resolve'); let result = cache.getSync(key); if (result === undefined) { result = resolve(targetModule, fromDir, options); cache.putSync(key, result || null); } return result || undefined; } return { resolve, resolveCached }; }; ================================================ FILE: src/resolve/parseRequire.js ================================================ module.exports = function parseRequire(path) { const typeSeparatorIndex = path.indexOf(':'); // NOTE: Windows paths may have drive letter followed by colon. // If colon is the second character then assume it is a // file system path. if (typeSeparatorIndex !== -1 && typeSeparatorIndex > 1 /* Fixes Issue: https://github.com/lasso-js/lasso/issues/65 */) { const type = path.substring(0, typeSeparatorIndex).trim(); path = path.substring(typeSeparatorIndex + 1).trim(); return { type, path }; } else { return { path }; } }; ================================================ FILE: src/transforms.js ================================================ const inspect = require('util').inspect; const ok = require('assert').ok; const DeferredReadable = require('./util').DeferredReadable; const PassThrough = require('stream').PassThrough; const through = require('through'); function Transformer(transforms) { this.transforms = transforms; } function handleNonStreamTransform(inStream, transform, applyTransform, handleError) { let code = ''; const outStream = through( function write(data) { code += data; }, function end() { function handleTransformedCode(transformedCode) { outStream.push(transformedCode); outStream.push(null); } const transformedCode = applyTransform(code, transform); if (transformedCode != null) { if (typeof transformedCode === 'string') { handleTransformedCode(transformedCode); } else if (typeof transformedCode.then === 'function') { transformedCode .then(function(transformedCode) { handleTransformedCode(transformedCode); }) .catch(handleError); } else { handleError(new Error('Invalid return for transform: ' + transformedCode)); } } }); return outStream; } Transformer.prototype = { hasTransforms () { return this.transforms.length !== 0; }, transform (inStream, lassoContext) { const transforms = this.transforms; if (!transforms.length) { return inStream; } ok(lassoContext, 'lassoContext is required'); ok(inStream, 'inStream is required'); const config = lassoContext.config; ok(config, 'config expected in context'); function applyTransform(input, transform) { return transform.transform(input, lassoContext); } return new DeferredReadable(function() { const deferredStream = this; function handleError(e) { deferredStream.emit('error', e); deferredStream.push(null); // End the stream just in case } inStream.on('error', handleError); const passThrough = new PassThrough(); let out = passThrough; for (let i = 0, len = transforms.length; i < len; i++) { const transform = transforms[i]; if (transform.stream === true) { // applyTransform will return a new stream that we can read from out = applyTransform(out, transform); if (typeof out.pipe !== 'function') { return handleError( new Error('Non-stream object returned from transform (transform=' + inspect(transform) + ', output=' + inspect(out) + ')')); } } else { // The transform doesn't want a stream so we have to do some additional processing... out = out.pipe(handleNonStreamTransform(out, transform, applyTransform, handleError)); } out.on('error', handleError); } // Add some listeners to the output stream returned by the final transform out.on('data', function(data) { deferredStream.push(data); }); out.on('end', function() { deferredStream.push(null); }); // Now start the flow of data at the source by piping the input stream // to the beginning of our transform chain (i.e. the initial pass thorugh stream) inStream.pipe(passThrough); }); } }; async function filterTransform (lassoContext, transformConfig) { return transformConfig.filter ? transformConfig.filter(lassoContext) : true; } exports.createTransformer = async function (unfilteredTransforms, lassoContext) { if (unfilteredTransforms) { ok(Array.isArray(unfilteredTransforms), 'unfilteredTransforms should be an array'); } const contentType = lassoContext.contentType; ok(typeof contentType === 'string', '"contentType" is required'); const filteredTransforms = []; for (const unfilteredTransform of unfilteredTransforms) { const keep = await filterTransform(lassoContext, unfilteredTransform); if (keep) { filteredTransforms.push(unfilteredTransform); } } return new Transformer(filteredTransforms); }; ================================================ FILE: src/util/CombinedStream.js ================================================ const Readable = require('stream').Readable; const inherit = require('raptor-util/inherit'); function CombinedStream(options) { CombinedStream.$super.call(this, options); const streams = this.streams = []; const combined = this; let separator; let curStream; let i = -1; let len; let paused = false; let reading = false; if (options) { separator = options.separator; delete options.separator; } function onError(err) { combined.emit('error', err); } function onData(chunk) { if (combined.push(chunk) === false) { paused = true; curStream.pause(); } } let depth = 0; /* Node.js internally uses process.nextTick() during stream data flow events. Node.js checks to make sure that process.nextTick() is not called with too much recursion because this will starve I/O event processing. The dependency streams may have read all of their data so process.nextTick() could be called recursively more than the allowed limit. The recommended approach for avoiding this limit is to use setImmediate() to give the event loop a chance to handle I/O events. Recursive process.nextTick detected" from within stream code #6065 https://github.com/joyent/node/issues/6065 stream: readable _read blocking script execution #7401 https://github.com/joyent/node/issues/7401 */ function cautiousNext() { depth++; if (depth > 100) { depth = 0; setImmediate(next); } else { next(); } } function next() { if (curStream) { combined.emit('endStream', { stream: curStream, index: i }); } if (++i >= len) { // we're done combined.push(null); } else { if (separator && curStream) { combined.push(separator); } combined.curStream = curStream = streams[i]; combined.emit('beginStream', { stream: curStream, index: i }); if (typeof curStream === 'string') { onData(curStream); cautiousNext(); } else { curStream.on('end', cautiousNext); curStream.on('error', onError); curStream.on('data', onData); // make sure the current stream is resumed curStream.resume(); } } } this._read = function() { if (reading) { if (paused) { curStream.resume(); } } else { reading = true; len = streams.length; if (len === 0) { combined.push(null); } else { cautiousNext(); } } }; } CombinedStream.prototype.addStream = function(stream) { this.streams.push(stream); }; CombinedStream.prototype.getStream = function(index) { return this.streams[index]; }; CombinedStream.prototype.getStreamCount = function(stream) { return this.streams.length; }; CombinedStream.prototype.forEachStream = function(fn) { return this.streams.forEach(fn); }; inherit(CombinedStream, Readable); module.exports = CombinedStream; ================================================ FILE: src/util/DeferredReadable.js ================================================ const Readable = require('stream').Readable; const inherit = require('raptor-util/inherit'); function DeferredReadable(startFn, options) { if (startFn && typeof startFn !== 'function') { options = startFn; startFn = null; } DeferredReadable.$super.call(this, options); let readCalled = false; let wrappedStream = null; let paused = false; const _this = this; let ended = false; let chunkCount = 0; let queuedEmits; this.on('ready', function (wrappedStream) { _this.setWrappedStream(wrappedStream); }); function startReadingWrappedStream() { wrappedStream .on('end', function() { ended = true; _this.push(null); }) .on('error', function(err) { _this.emit('error', err); }) .on('data', function(data) { chunkCount++; if (_this.push(data) === false) { paused = true; wrappedStream.pause(); } }) .resume(); } this.emit = function(type) { if (readCalled || type !== 'error') { Readable.prototype.emit.apply(this, arguments); } else { // Queue up error events if we haven't started reading if (!queuedEmits) { queuedEmits = [arguments]; } else { queuedEmits.push(arguments); } } }; this._read = function() { if (readCalled) { if (wrappedStream && paused) { paused = false; wrappedStream.resume(); } } else { readCalled = true; if (queuedEmits) { for (let i = 0, len = queuedEmits.length; i < len; i++) { Readable.prototype.emit.apply(this, queuedEmits[i]); } queuedEmits = undefined; } if (wrappedStream) { startReadingWrappedStream(); } else if (startFn) { const result = startFn.call(this); if (result) { this.setWrappedStream(result); } } } }; this.setWrappedStream = function(stream) { wrappedStream = stream; if (readCalled) { startReadingWrappedStream(); } }; this.toString = function() { return '[DeferredReadable readCalled=' + readCalled + ', ended=' + ended + ', chunkCount=' + chunkCount + ', wrappedStream=' + wrappedStream + ']'; }; } inherit(DeferredReadable, Readable); module.exports = DeferredReadable; ================================================ FILE: src/util/caching-stream.js ================================================ const inherit = require('raptor-util/inherit'); const Transform = require('stream').Transform; const Readable = require('stream').Readable; // ReplayStream function ReplayStream(cachingStream, options) { ReplayStream.$super.call(this, options); const _this = this; const _chunks = cachingStream._chunks; let _pos = 0; function continueReading() { while (_pos < _chunks.length) { if (_this.push(_chunks[_pos++]) === false) { break; } } } if (cachingStream._finished === false) { cachingStream .on('_newData', continueReading) .once('end', function() { cachingStream.removeListener('_newData', continueReading); }); } this._read = continueReading; this.toString = function(size) { return '[ReplayStream chunkCount=' + _chunks.length + ', pos=' + _pos + ']'; }; } inherit(ReplayStream, Readable); // CachingStream function CachingStream(options) { CachingStream.$super.call(this, options); this._chunks = []; this._finished = false; } CachingStream.prototype._transform = function(chunk, encoding, callback) { // console.log(module.id, 'caching stream push: '÷, chunk.toString('utf8')); this._chunks.push(chunk); // Cache the chunk this.emit('_newData'); this.push(chunk); callback(); }; CachingStream.prototype._flush = function(callback) { // console.log(module.id, 'caching stream end'); this._chunks.push(null); this.emit('_newData'); this._finished = true; callback(); }; CachingStream.prototype.createReplayStream = function(options) { return new ReplayStream(this, options); }; CachingStream.prototype.toString = function() { return '[CachingStream len=' + this._chunks.length + ']'; }; inherit(CachingStream, Transform); exports.create = function(options) { return new CachingStream(options); }; ================================================ FILE: src/util/fingerprint-stream.js ================================================ const crypto = require('crypto'); const inherit = require('raptor-util/inherit'); const Transform = require('stream').Transform; function FingerprintStream(options) { FingerprintStream.$super.call(this, options); this._shasum = crypto.createHash('sha1'); this._fingerprint = null; } FingerprintStream.prototype._transform = function(chunk, encoding, callback) { this._shasum.update(chunk); this.push(chunk); callback(); }; FingerprintStream.prototype._flush = function(callback) { this._fingerprint = this._shasum.digest('hex'); this._shasum = null; this.emit('fingerprint', this._fingerprint); callback(); }; FingerprintStream.prototype.on = function(event, callback) { if (event === 'fingerprint' && this._fingerprint) { callback.call(this, this._fingerprint); return this; } else { return FingerprintStream.$super.prototype.on.apply(this, arguments); } }; inherit(FingerprintStream, Transform); exports.create = function() { return new FingerprintStream(); }; ================================================ FILE: src/util/hash.js ================================================ const crypto = require('crypto'); exports.HASH_OVERFLOW_LENGTH = 8; exports.generate = function (str, len) { let hash = crypto.createHash('sha1') .update(str) .digest('hex'); if (len != null) hash = hash.substring(0, len); return hash; }; ================================================ FILE: src/util/index.js ================================================ const cachingStream = require('./caching-stream'); const fingerprintStream = require('./fingerprint-stream'); const DeferredReadable = require('./DeferredReadable'); const logger = require('raptor-logging').logger(module); // var DEFAULT_READ_FILE_OPTIONS = {encoding: 'utf8'}; function merge(src, dest) { if (src != null && dest != null && !Array.isArray(src) && !Array.isArray(dest) && typeof src === 'object' && typeof dest === 'object') { Object.getOwnPropertyNames(src) .forEach(function(prop) { const descriptor = Object.getOwnPropertyDescriptor(src, prop); descriptor.value = merge(descriptor.value, dest[prop]); Object.defineProperty(dest, prop, descriptor); }); return dest; } return src; } function streamToString(stream, callback) { let str = ''; stream.on('data', function(data) { str += data; }); stream.on('error', function(err) { callback(err); }); stream.on('end', function() { callback(null, str); }); } function readStream(func) { // Calling read will do one of the following // 1) Return the actual value or null if there no data // 2) Invoke our callback with a value // 3) Return a stream const stream = new DeferredReadable(function() { // this function will be called when it is time to start reading data let finished = false; function callback(err, value) { if (finished) { logger.warn(new Error('read callback invoked after finish')); return; } if (err) { stream.emit('error', err); return; } if (value == null) { stream.push(null); finished = true; } else { if (typeof value === 'string') { stream.push(value); stream.push(null); finished = true; } else if (typeof value.pipe === 'function') { // Looks like a stream... value.pipe(this); finished = true; } else if (typeof value.then === 'function') { // Looks like a promise... value .then((value) => { callback(null, value); }) .catch(callback); } else { // Hopefully a Buffer stream.push(value); stream.push(null); finished = true; } } }; const result = func(callback); if (!finished) { // callback was not invoked if (result === null) { callback(null, null); } else if (result === undefined) { // waiting on callback } else if (result && typeof result.pipe === 'function') { finished = true; return result; } else if (result && typeof result.then === 'function') { result.then((promiseResult) => { if (promiseResult && typeof promiseResult.pipe === 'function') { finished = true; stream.emit('ready', promiseResult); } else { callback(null, promiseResult); } }).catch((err) => { callback(err); }); } else { callback(null, result); // A stream was returned, so we will return it } } }); return stream; } exports.merge = merge; exports.streamToString = streamToString; exports.createCachingStream = cachingStream.create; exports.createFingerprintStream = fingerprintStream.create; exports.readStream = readStream; exports.DeferredReadable = require('./DeferredReadable'); ================================================ FILE: src/util/prebuild.js ================================================ const nodePath = require('path'); exports.buildPrebuildFileName = function (file) { return `${file}.prebuild.json`; }; exports.buildPrebuildName = function (pagePath) { const name = pagePath && nodePath.basename(pagePath); if (name) { const extLen = nodePath.extname(name).length; return (extLen && name.slice(0, 0 - extLen)) || name; } return 'page'; }; ================================================ FILE: src/util/stringify-attrs.js ================================================ module.exports = function stringifyAttributes (obj) { let str = ''; for (const key in obj) { const val = obj[key]; if (val === false || val == null) { continue; } str += ' ' + key; if (val !== true) { str += '=' + JSON.stringify(val); } } return str; }; ================================================ FILE: src/util/url-reader.js ================================================ const http = require('http'); const https = require('https'); const DeferredReadable = require('./DeferredReadable'); function createUrlReadStream(url) { const stream = new DeferredReadable(function() { const parsedUrl = new URL(url, 'file:'); const isSecure = parsedUrl.protocol === 'https:'; const get = isSecure ? https.get : http.get; const options = { hostname: parsedUrl.hostname, port: parsedUrl.port || (isSecure ? 443 : 80), path: parsedUrl.pathname + (parsedUrl.search ? parsedUrl.search : '') }; const req = get(options, function(res) { if (res.statusCode < 200 || res.statusCode >= 300) { stream.emit('error', 'Request to ' + url + ' failed with a HTTP status code ' + res.statusCode); return; } res .on('end', function() { stream.push(null); }) .on('error', function(err) { stream.emit('error', err); }) .on('data', function(data) { stream.push(data); }); }); req.on('error', function(err) { stream.emit('error', 'Request to ' + url + ' failed. Error: ' + (err.stack || err)); }); }, { encoding: 'utf8' }); return stream; } exports.createUrlReadStream = createUrlReadStream; ================================================ FILE: src/writers/Writer.js ================================================ const nodePath = require('path'); const fileSep = nodePath.sep; const logger = require('raptor-logging').logger(module); const EventEmitter = require('events').EventEmitter; const extend = require('raptor-util').extend; const createError = require('raptor-util/createError'); const reader = require('../reader'); const ok = require('assert').ok; const equal = require('assert').equal; async function initWriter (writer, lassoContext) { if (!writer._initialized && writer.impl.init) { await writer.impl.init(lassoContext); writer._initialized = true; } } function Writer(impl) { Writer.$super.call(this); this.impl = impl || {}; // Lasso writer `init` function should only be called once. We call it the // first time that either writing a resource or bundle is attempted. this._initialized = false; } Writer.prototype = { __LassoWriter: true, setLasso: function(lasso) { this.lasso = lasso; }, getLasso: function() { return this.lasso; }, getInPlaceUrlForFile: function(path, lassoContext) { ok(lassoContext, 'lassoContext is required'); if (typeof path !== 'string') { throw new Error('Path for in-place file should be a string. Actual: ' + path); } const config = lassoContext.config; if (typeof config.getInPlaceUrlPrefix() === 'string') { const projectRoot = config.getProjectRoot(); if (projectRoot && path.startsWith(projectRoot + fileSep)) { const suffix = path.substring(projectRoot.length); return config.getInPlaceUrlPrefix() + suffix; } } return null; }, getInPlaceUrlForBundle: function(bundle, lassoContext) { ok(lassoContext, 'lassoContext is required'); const dependency = bundle.dependency; if (!dependency) { throw new Error('"dependency" expected for in-place bundle'); } if (!dependency.getSourceFile) { throw new Error('"getSourceFile" expected for in-place dependency'); } if (!bundle.inPlaceDeployment) { throw new Error('inPlaceDeployment should be true'); } const sourceFile = dependency.getSourceFile(); return this.getInPlaceUrlForFile(sourceFile, lassoContext); }, async writeBundle (bundle, onBundleWrittenCallback, lassoContext) { if (!bundle.hasContent()) return; ok(lassoContext, 'lassoContext is required'); const done = (err) => { if (err) { throw createError('Error while writing bundle "' + bundle + '" Error: ' + err, err); } bundle.setWritten(true); if (onBundleWrittenCallback) { onBundleWrittenCallback(bundle); } const data = { bundle }; this.emit('bundleWritten', data); lassoContext.emit('bundleWritten', data); logger.info('Bundle ' + bundle + ' written.'); return bundle; }; if (bundle.isWritten() || bundle.url) { if (logger.isInfoEnabled()) { logger.info('Bundle (' + bundle.getKey() + ') already written. Skipping writing...'); } return done(); } else if ((bundle.inPlaceDeployment === true) && !bundle.isInline()) { const inPlaceUrl = this.getInPlaceUrlForBundle(bundle, lassoContext); if (inPlaceUrl) { if (logger.isInfoEnabled()) { logger.info('In-place deployment enabled for (' + bundle.getKey() + '). Skipping writing...'); } bundle.setUrl(inPlaceUrl); return done(); } } lassoContext = Object.create(lassoContext); lassoContext.bundle = bundle; lassoContext.dependencies = bundle.dependencies; const bundleReader = reader.createBundleReader(bundle, lassoContext); logger.info('Writing bundle ' + bundle + '...'); const checkBundleUpToDate = async () => { if (bundle.isInline()) return; // We make the assumption that the bundle was populated with its URL // and marked as written if it was indeed up-to-date return this.checkBundleUpToDate(bundle, lassoContext); }; const writeBundle = async () => { // If the bundle is written then there is nothing to do if (bundle.isWritten()) return; let completed = false; function handleError(e) { if (!completed) { completed = true; throw e; } } if (bundle.isInline()) { try { const code = await bundleReader.readBundleFully(); logger.info('Code for inline bundle ' + bundle.getLabel() + ' generated.'); bundle.setCode(code); } catch (err) { return handleError(err); } } else { try { await this.impl.writeBundle(bundleReader, lassoContext); logger.info('Bundle written:', bundle.getLabel()); } catch (err) { return handleError(err); } } }; await checkBundleUpToDate(); await writeBundle(); return done(); }, async writeResource (path, lassoContext) { ok(lassoContext, 'lassoContext is required'); const done = (writeResult) => { ok(writeResult, 'writeResult expected'); ok(writeResult.url, 'writeResult.url expected'); const result = extend(writeResult || {}, { sourceFile: path }); this.emit('resourceWritten', result); lassoContext.emit('resourceWritten', result); return result; }; const config = this.config; if (config.isInPlaceDeploymentEnabled()) { const url = this.getInPlaceUrlForFile(path, lassoContext); if (url) { return done({ url }); } } lassoContext = Object.create(lassoContext); lassoContext.path = path; const resourceReader = reader.createResourceReader(path, lassoContext); try { await initWriter(this, lassoContext); const writeResult = await this.impl.writeResource(resourceReader, lassoContext); return done(writeResult); } catch (err) { throw createError('Error while writing resource "' + path + '": ' + (err.stack || err), err); } // this.checkResourceUpToDate(path, lassoContext, function(err, resourceInfo) { // if (err) { // return callback(err); // } // // if (resourceInfo) { // return callback(null, resourceInfo); // } // // // }); }, async writeResourceBuffer (buff, path, lassoContext) { ok(lassoContext, 'lassoContext is required'); const done = (writeResult) => { ok(writeResult, 'writeResult expected'); ok(writeResult.url, 'writeResult.url expected'); writeResult = writeResult || {}; this.emit('resourceWritten', writeResult); lassoContext.emit('resourceWritten', writeResult); return writeResult; }; lassoContext = Object.create(lassoContext); lassoContext.path = path; try { await initWriter(this, lassoContext); const writeResult = await this.impl.writeResourceBuffer(buff, lassoContext); return done(writeResult); } catch (err) { throw createError('Error while writing resource buffer: ', err); } }, async checkBundleUpToDate (bundle, lassoContext) { ok(lassoContext, 'lassoContext is required'); if (this.impl.checkBundleUpToDate) { const resourceInfo = await this.impl.checkBundleUpToDate(bundle, lassoContext); return resourceInfo === false ? null : resourceInfo; } }, checkResourceUpToDate: function(path, lassoContext, callback) { ok(lassoContext, 'lassoContext is required'); equal(typeof callback, 'function', 'callback function is required'); if (this.impl.checkResourceUpToDate) { this.impl.checkResourceUpToDate(path, lassoContext, function(err, resourceInfo) { if (err) { return callback(err); } if (resourceInfo === false) { resourceInfo = null; } return callback(null, resourceInfo); }); } else { return callback(); } }, async writeBundles (iteratorFunc, onBundleWrittenCallback, lassoContext) { ok(lassoContext, 'lassoContext is required'); await initWriter(this, lassoContext); let promise = Promise.resolve(); iteratorFunc((bundle) => { if (bundle.hasContent()) { promise = promise.then(() => { return this.writeBundle(bundle, onBundleWrittenCallback, lassoContext); }); } }); return promise; }, buildResourceCacheKey(cacheKey, lassoContext) { if (this.impl.buildResourceCacheKey) { return this.impl.buildResourceCacheKey(cacheKey, lassoContext); } else { return cacheKey; } } }; require('raptor-util').inherit(Writer, EventEmitter); module.exports = Writer; ================================================ FILE: src/writers/file-writer.js ================================================ const MAX_FILE_LENGTH = 255; const util = require('../util'); const nodePath = require('path'); const fs = require('fs'); const ok = require('assert').ok; const logger = require('raptor-logging').logger(module); const mkdirp = require('mkdirp'); const crypto = require('crypto'); const raptorAsync = require('raptor-async'); const Duplex = require('stream').Duplex; const hashUtil = require('../util/hash'); function filePathToUrlWindows(path) { return path.replace(/[\\]/g, '/'); } function filePathToUrlUnix(path) { return path; } function enforceFileLengthLimits(path) { return path.split(nodePath.sep).map(part => { if (part.length < MAX_FILE_LENGTH) return part; const overflow = part.slice(MAX_FILE_LENGTH - hashUtil.HASH_OVERFLOW_LENGTH); const hash = hashUtil.generate(overflow); return part.slice(0, MAX_FILE_LENGTH - hashUtil.HASH_OVERFLOW_LENGTH) + hash.slice(0, hashUtil.HASH_OVERFLOW_LENGTH); }).join(nodePath.sep); } const filePathToUrl = nodePath.sep === '/' ? filePathToUrlUnix : filePathToUrlWindows; /** * Utility function to generate a random string of characters * suitable for use in a filename. This function is needed * to generate a temporary file name * * @param {int} len The length of the character sequence * @return {String} The random characters with the specified length */ function randomStr(len) { return crypto.randomBytes(Math.ceil(len / 2)) .toString('hex') // convert to hexadecimal format .slice(0, len); // return required number of characters } /** * This "getUrl(lassoContext)" function is added to a bundle instance * to provide a mechanism to generate a bundle URL based on a * base path that might change. Unless a URL prefix is specififed * the URL is generated relative to a base path or the CWD. * * @param {Object} lassoContext An object with lassoContextual information needed to generate a URL */ function getBundleUrl(lassoContext) { let url = this.url; if (url) { return url; } const outputFile = this.outputFile; let urlPrefix = this.urlPrefix; const outputDir = this.outputDir; ok(lassoContext.config, 'lassoContext.config expected'); ok(outputFile, 'outputFile expected'); if (typeof urlPrefix === 'string') { const relPath = filePathToUrl(outputFile.substring(outputDir.length)); if (urlPrefix.endsWith('/')) { urlPrefix = urlPrefix.slice(0, -1); } url = urlPrefix + relPath; return url; } else { const basePath = lassoContext.basePath ? nodePath.resolve(process.cwd(), lassoContext.basePath) : process.cwd(); return filePathToUrl(nodePath.relative(basePath, outputFile)); } } /** * Internal function help write out a file and to possibly generate a fingerprint * in the process if fingerprints are enabled. * * On success, the callback will be invoked with an object that contains the following * properties: * - fingerprint: The string fingerprint if calculateFingerprint is set to true * - outputFile: The output file. If calculateFingerprint is set to true then the fingerprint * will be injected into the filename * * * @param {ReadableStream} inStream The input stream to read from * @param {string} outputFile The output file path * @param {boolean} calculateFingerprint If true then a fingerprint will be calculated and passed to the callback * @return void */ async function writeFile (inStream, outputFile, calculateFingerprint, fingerprintLength) { const outputDir = nodePath.dirname(outputFile); let done = false; await mkdirp(outputDir); let outStream; const tempFile = outputFile + '.' + process.pid + '.' + randomStr(4); return new Promise((resolve, reject) => { function handleError(err) { if (done) { return; } done = true; reject(err); } function handleSuccess(result) { if (done) { return; } done = true; resolve(result); } if (calculateFingerprint) { logger.debug(`Writing bundle to temp file ${tempFile}... (calculating fingerprint)`); // Pipe the stream to a temporary file and when the fingerprint is known, // rename the file to include the known fingerprint outStream = fs.createWriteStream(tempFile); const fingerprintStream = util.createFingerprintStream(); let fingerprint; outStream .on('close', function() { if (done) { return; } if (fingerprintLength && fingerprint.length > fingerprintLength) { fingerprint = fingerprint.substring(0, fingerprintLength); } const ext = nodePath.extname(outputFile); outputFile = outputFile.slice(0, 0 - ext.length) + '-' + fingerprint + ext; fs.stat(outputFile, function (error, stats) { if (error) { fs.rename(tempFile, outputFile, function(err) { if (err && !fs.existsSync(outputFile)) { return handleError(err); } handleSuccess({ fingerprint, outputFile }); }); } else { // If it already exists then just use that file, but delete the temp file fs.unlink(tempFile, function() { handleSuccess({ fingerprint, outputFile }); }); } }); }); fingerprintStream .on('fingerprint', function(_fingerprint) { fingerprint = _fingerprint; }) .on('error', handleError) .pipe(outStream); inStream .on('error', handleError) .pipe(fingerprintStream); } else { logger.debug(`Writing bundle to temp file ${tempFile}... (no fingerprint)`); // No fingerprint is needed so simply pipe out the input stream // to the output file outStream = fs.createWriteStream(tempFile); inStream .on('error', handleError) .pipe(outStream) .on('close', function() { if (done) { return; } fs.rename(tempFile, outputFile, function(err) { if (err && !fs.existsSync(outputFile)) { return handleError(err); } handleSuccess({ outputFile }); }); }); } }); } module.exports = function fileWriter(fileWriterConfig, lassoConfig) { // The directory to place the built bundle and resource files const outputDir = nodePath.resolve(process.cwd(), fileWriterConfig.outputDir || 'static'); // Boolean value to indicate if including fingerprints in the output files is enabled // or not. const fingerprintsEnabled = fileWriterConfig.fingerprintsEnabled !== false; // Optional URL prefix to use when generating URLs to the bundled files let urlPrefix = fileWriterConfig.urlPrefix; // Boolean value to indicate if the target slot should be added to the output filename // e.g. "head" or "body" const includeSlotNames = fileWriterConfig.includeSlotNames; // If fingerprints are enabled then this flag will be used to determine how many characters // the fingerprint should contain const fingerprintLength = fileWriterConfig.fingerprintLength || 8; // Boolean value to indicate if CSS URLs should be relative const relativeUrlsEnabled = fileWriterConfig.relativeUrlsEnabled; /** * Calculate the output file for a given bundle given * the configuration for the file writer. * * @param {lasso/src/Bundle} bundle The lasso Bundle * @return {String} The output file path for the bundle */ function getOutputFileForBundle(bundle) { if (bundle.outputFile) { return bundle.outputFile; } let relativePath; if (bundle.dependency && bundle.dependency.getSourceFile) { relativePath = bundle.dependency.getSourceFile(); } else if (bundle.relativeOutputPath) { // We are being told to use a relative path that // we should join with the output directory relativePath = bundle.relativeOutputPath; } const targetExt = bundle.getContentType(); return getOutputFile( relativePath, bundle.getName(), targetExt, bundle.getSlot()); } function getOutputFileFromFileName (path, relativePath) { const filename = nodePath.basename(path); return getOutputFile( relativePath, filename); } function getOutputFileForResource(path, fingerprintsEnabled, lassoContext) { let relativePath; if (lassoConfig.isBundlingEnabled() === false || fingerprintsEnabled === false) { // When bundling is disabled we maintain the directory structure // so we want to provide the sourceFile so that we can // know which deeply nested resource path to use relativePath = lassoContext.getClientPath(path); const pageName = lassoContext.pageName; const flags = lassoContext.flags; if (pageName) { let prefix = pageName.replace(/[\\\/]/g, '-'); if (flags && !flags.isEmpty()) { prefix += '-' + flags.getKey(); } relativePath = prefix + '/' + relativePath; } } return getOutputFileFromFileName(path, relativePath); } function buildResourceCacheKey(cacheKey, lassoContext) { const lassoConfig = lassoContext.config; const pageName = lassoContext.pageName; if (pageName && (lassoConfig.isBundlingEnabled() === false || fingerprintsEnabled === false)) { return pageName + '-' + cacheKey; } return cacheKey; } function getOutputFile(relativePath, filename, targetExt, slotName) { let outputPath; if (relativePath) { outputPath = nodePath.join(outputDir, relativePath); } if (!outputPath) { ok(filename, '"filename" or "sourceFile" expected'); outputPath = nodePath.join(outputDir, filename.replace(/^\//, '').replace(/[^A-Za-z0-9_\-\.]/g, '-')); } const dirname = nodePath.dirname(outputPath); let basename = nodePath.basename(outputPath); const lastDot = basename.lastIndexOf('.'); let ext; let nameNoExt; if (lastDot !== -1) { ext = basename.substring(lastDot + 1); nameNoExt = basename.substring(0, lastDot); } else { ext = ''; nameNoExt = basename; } if (includeSlotNames && slotName) { nameNoExt += '-' + slotName; } basename = nameNoExt; if (ext) { basename += '.' + ext; } if (targetExt && ext != targetExt) { // eslint-disable-line eqeqeq basename += '.' + targetExt; } return enforceFileLengthLimits(nodePath.join(dirname, basename)); } function getResourceUrlForHashed (path, lassoContext) { let basePath; if (lassoContext && lassoContext.bundle && lassoContext.bundle.isStyleSheet() && relativeUrlsEnabled !== false) { // We should calculate a relative path from the CSS bundle to the resource bundle basePath = nodePath.dirname(getOutputFileForBundle(lassoContext.bundle)); return filePathToUrl(nodePath.relative(basePath, path)); } if (typeof urlPrefix === 'string') { const relPath = filePathToUrl(path.substring(outputDir.length)); if (urlPrefix.endsWith('/')) { urlPrefix = urlPrefix.slice(0, -1); } return urlPrefix + relPath; } else { basePath = lassoContext.basePath ? nodePath.resolve(process.cwd(), lassoContext.basePath) : process.cwd(); return filePathToUrl(nodePath.relative(basePath, path)); } } function getResourceUrl (path, lassoContext) { ok(path, 'path is required'); ok(path.startsWith(outputDir), 'resource expected to be in the output directory. path=' + path + ', outputDir=' + outputDir); return getResourceUrlForHashed(path, lassoContext); } return { buildResourceCacheKey, /** * This method is used to determine if writing a bundle * should be bypassed. * * @param {lasso/src/Bundle} The bundle instance * @param {Object} Contextual information * @return {[type]} [description] */ async checkBundleUpToDate (bundle, lassoContext) { return false; // NOTE: We used to do a last modified check based on file modified datas, // but there were edge cases that caused problem. For example, even though // none of the files in the bundle were modified, a separate file that impacts // how the bundle code may be generated may have been modified. The // saves from a timestamp check are minimal. }, checkResourceUpToDate: function(path, lassoContext, callback) { if (fingerprintsEnabled) { return callback(null, false); } const outputFile = getOutputFileForResource(path, fingerprintsEnabled, lassoContext); const work = { async sourceLastModified () { return lassoContext.getFileLastModified(path); }, async outputLastModified (callback) { return lassoContext.getFileLastModified(outputFile); } }; raptorAsync.parallel(work, function(err, results) { if (err) { return callback(err); } if (results.outputLastModified >= results.sourceLastModified) { // The resource has not been modified so let the lasso // know what URL to use for the resource const url = getResourceUrl(outputFile, lassoContext); callback(null, { url, outputFile }); } else { // Resource is not up-to-date and needs to be written callback(null, false); } }); }, async writeBundle (reader, lassoContext) { return new Promise((resolve, reject) => { const input = reader.readBundle(); const bundle = lassoContext.bundle; ok(input, '"input" is required'); ok(bundle, '"bundle" is required'); input.on('error', reject); let calculateFingerprint = bundle.config.fingerprintsEnabled; if (calculateFingerprint === undefined) { calculateFingerprint = fingerprintsEnabled; } calculateFingerprint = calculateFingerprint === true && !bundle.getFingerprint(); const outputFile = getOutputFileForBundle(bundle); logger.debug('Writing bundle "' + bundle.getLabel() + '" to file "' + outputFile + '"...'); writeFile(input, outputFile, calculateFingerprint, fingerprintLength) .then((result) => { logger.debug('Writing bundle "' + bundle.getLabel() + '" to file "' + outputFile + '" COMPLETED'); bundle.setFingerprint(result.fingerprint); bundle.setWritten(true); bundle.getUrl = getBundleUrl; bundle.urlPrefix = urlPrefix; bundle.outputDir = outputDir; bundle.outputFile = result.outputFile; resolve(); }).catch((err) => { logger.debug('Writing bundle "' + bundle.getLabel() + '" to file "' + outputFile + '" FAILED! - Error:', err); reject(err); }); }); }, async writeResource (reader, lassoContext) { let done = false; const input = reader.readResource(); const path = lassoContext.path; ok(input, '"input" is required'); ok(path, '"path" is required'); return new Promise((resolve, reject) => { function handleError (err) { if (done) { return; } done = true; reject(err); } input.on('error', handleError); const calculateFingerprint = fingerprintsEnabled === true; const outputFileForResource = getOutputFileForResource(path, calculateFingerprint, lassoContext); writeFile( input, outputFileForResource, calculateFingerprint, fingerprintLength) .then((result) => { const outputFile = result.outputFile; const url = getResourceUrl(outputFile, lassoContext); resolve({ url, outputFile }); }).catch(handleError); }); }, async writeResourceBuffer (buff, lassoContext) { let done = false; const input = new Duplex(); input.push(buff); input.push(null); const path = lassoContext.path; ok(input, '"input" is required'); ok(path, '"path" is required'); return new Promise((resolve, reject) => { function handleError (err) { if (done) { return; } done = true; reject(err); } input.on('error', handleError); const calculateFingerprint = false; const outputFileForResource = getOutputFileFromFileName(path, null); writeFile( input, outputFileForResource, calculateFingerprint, fingerprintLength) .then((result) => { const outputFile = result.outputFile; const url = getResourceUrlForHashed(outputFile, lassoContext); resolve({ url, outputFile }); }).catch(handleError); }); } }; }; ================================================ FILE: src/writers/index.js ================================================ const Writer = require('./Writer'); function createWriter(writerImpl) { return new Writer(writerImpl); } exports.Writer = Writer; exports.fileWriter = require('./file-writer'); exports.createWriter = createWriter; ================================================ FILE: test/.eslintrc ================================================ { "globals": { "it": 1, "describe": 1, "context": 1, "beforeEach": 1, "afterEach": 1 } } ================================================ FILE: test/.gitignore ================================================ *.marko.js *.dust.js ~*/ actual.* actual-* ================================================ FILE: test/api-test.js ================================================ 'use strict'; require('./util/test-init'); var nodePath = require('path'); require('chai').config.includeStack = true; var rmdirRecursive = require('./util').rmdirRecursive; var lasso = require('lasso'); var buildDir = nodePath.join(__dirname, 'build'); let _log = console.log; describe('lasso/api' , function() { require('./autotest').scanDir( nodePath.join(__dirname, 'autotests/api'), async function (dir, helpers) { var name = nodePath.basename(dir); var outputDir = nodePath.join(buildDir, name); rmdirRecursive(outputDir); helpers.getName = function() { return name; }; helpers.getOutputDir = function() { return outputDir; }; var main = require(nodePath.join(dir, 'test.js')); return main.check(lasso, helpers); }); }); ================================================ FILE: test/autotest.js ================================================ 'use strict'; var fs = require('fs'); var enabledTest = process.env.TEST; var path = require('path'); var assert = require('assert'); var enabledTestNames = enabledTest && enabledTest.split(/[\s*,\s*/]/); var enabledTests = null; if (enabledTestNames && enabledTestNames.length > 1) { enabledTests = {}; enabledTest = null; enabledTestNames.forEach((testName) => { enabledTests[testName] = true; }); } var fs = require('fs'); var enabledTest = process.env.TEST; var path = require('path'); var assert = require('assert'); function compareHelper(dir, actual, suffix) { var actualPath = path.join(dir, 'actual' + suffix); var expectedPath = path.join(dir, 'expected' + suffix); var isObject = typeof actual === 'string' ? false : true; var actualString = isObject ? JSON.stringify(actual, null, 4) : actual; fs.writeFileSync(actualPath, actualString, { encoding: 'utf8' }); var expectedString; try { expectedString = fs.readFileSync(expectedPath, { encoding: 'utf8' }); } catch(e) { expectedString = isObject ? '"TBD"' : 'TBD'; fs.writeFileSync(expectedPath, expectedString, {encoding: 'utf8'}); } if (isObject) { actual = JSON.parse(actualString); } var expected = isObject ? JSON.parse(expectedString) : expectedString; assert.deepEqual(actual, expected); } function autoTest (name, dir, run, options) { options = options || {}; var helpers = { compare(actual, suffix) { compareHelper(dir, actual, suffix); } }; return run(dir, helpers); } exports.scanDir = function(autoTestDir, run, options) { describe('autotest', function() { fs.readdirSync(autoTestDir) .forEach(function(name) { if (name.charAt(0) === '.') { return; } if (enabledTests && !enabledTests[name]) { return; } var itFunc = it; if (enabledTest && name === enabledTest) { itFunc = it.only; } if (name.endsWith('-skip')) { itFunc = it.skip; } var dir = path.join(autoTestDir, name); itFunc(`[${name}] `, function () { return autoTest(name, dir, run, options); }); }); }); }; ================================================ FILE: test/autotests/api/config-fingerprint/foo.js ================================================ console.log('Foo!'); ================================================ FILE: test/autotests/api/config-fingerprint/test.js ================================================ const expect = require('chai').expect; exports.check = function (lasso, helpers) { var config = { bundles: [ { name: 'foo', dependencies: [ "require: " + require.resolve('./foo.js') ] } ] }; var myLasso1 = lasso.create(config); var myLasso2 = lasso.create(config); expect(myLasso1.config.getConfigFingerprint()).to.equal(myLasso2.config.getConfigFingerprint()); }; ================================================ FILE: test/autotests/api/lasso-lassoPage-promise/browser.json ================================================ { "dependencies": [ "./foo.js" ] } ================================================ FILE: test/autotests/api/lasso-lassoPage-promise/foo.js ================================================ foo ================================================ FILE: test/autotests/api/lasso-lassoPage-promise/test.js ================================================ const nodePath = require('path'); const expect = require('chai').expect; exports.check = async function (lasso, helpers) { lasso.configure({ bundlingEnabled: false, fingerprintsEnabled: false, outputDir: helpers.getOutputDir() }); const lassoPageResult = await lasso.lassoPage({ pageName: helpers.getName(), dependencies: [ nodePath.join(__dirname, 'browser.json') ] }); expect(lassoPageResult.getOutputFilesWithInfo().length).to.equal(1); }; ================================================ FILE: test/autotests/api/lasso-lassoResource-buffer/test.js ================================================ 'use strict'; const fs = require('fs'); const nodePath = require('path'); const expect = require('chai').expect; exports.check = async function(lasso, helpers) { lasso.configure({ bundlingEnabled: true, fingerprintsEnabled: true, outputDir: helpers.getOutputDir() }); const imgPath = nodePath.join(__dirname, 'ebay.png'); const buffer = await fs.promises.readFile(imgPath); const result = await lasso.lassoResource(buffer, { extension: 'png' }); expect(result.url).to.equal('/static/cf6691ad.png'); }; ================================================ FILE: test/autotests/api/lasso-lassoResource-buffer-name/test.js ================================================ 'use strict'; const fs = require('fs'); const nodePath = require('path'); const expect = require('chai').expect; exports.check = async function(lasso, helpers) { lasso.configure({ bundlingEnabled: true, fingerprintsEnabled: true, outputDir: helpers.getOutputDir() }); const imgPath = nodePath.join(__dirname, 'ebay.png'); const buffer = await fs.promises.readFile(imgPath); const result = await lasso.lassoResource(buffer, { name: 'test', extension: 'png' }); expect(result.url).to.equal('/static/test-cf6691ad.png'); }; ================================================ FILE: test/autotests/api/lasso-lassoResource-promise/foo.txt ================================================ foo ================================================ FILE: test/autotests/api/lasso-lassoResource-promise/test.js ================================================ const nodePath = require('path'); const expect = require('chai').expect; exports.check = async function (lasso, helpers) { lasso.configure({ bundlingEnabled: true, fingerprintsEnabled: true, outputDir: helpers.getOutputDir() }); const result = await lasso.lassoResource(nodePath.join(__dirname, 'foo.txt')); expect(result.url).to.equal('/static/foo-0beec7b5.txt'); }; ================================================ FILE: test/autotests/api/myLasso-lassoPage-promise/browser.json ================================================ { "dependencies": [ "./foo.js" ] } ================================================ FILE: test/autotests/api/myLasso-lassoPage-promise/foo.js ================================================ foo ================================================ FILE: test/autotests/api/myLasso-lassoPage-promise/test.js ================================================ var nodePath = require('path'); var expect = require('chai').expect; exports.check = async function (lasso, helpers) { var myLasso = lasso.create({ bundlingEnabled: false, fingerprintsEnabled: false, outputDir: helpers.getOutputDir() }); const lassoPageResult = await myLasso.lassoPage({ pageName: helpers.getName(), dependencies: [ nodePath.join(__dirname, 'browser.json') ] }); expect(lassoPageResult.getOutputFilesWithInfo().length).to.equal(1); }; ================================================ FILE: test/autotests/api/myLasso-lassoResource-promise/foo.txt ================================================ foo ================================================ FILE: test/autotests/api/myLasso-lassoResource-promise/test.js ================================================ const nodePath = require('path'); const expect = require('chai').expect; exports.check = async function (lasso, helpers) { const myLasso = lasso.create({ bundlingEnabled: true, fingerprintsEnabled: true, outputDir: helpers.getOutputDir() }); const result = await myLasso.lassoResource(nodePath.join(__dirname, 'foo.txt')); expect(result.url).to.equal('/static/foo-0beec7b5.txt'); }; ================================================ FILE: test/autotests/api/pollute-default-config/test.js ================================================ const expect = require('chai').expect; exports.check = function (lasso, helpers) { const myLasso1 = lasso.create({ require: { test: 'abc' } }); const myLasso2 = lasso.create({ }); const defaultLasso = lasso.getDefaultLasso(); const requirePlugin1 = myLasso1.getConfig().getPlugins()[0]; const requirePlugin2 = myLasso2.getConfig().getPlugins()[0]; const requirePlugin3 = defaultLasso.getConfig().getPlugins()[0]; expect(requirePlugin1.config.test).to.equal('abc'); expect(requirePlugin2.config.test).to.not.exist; expect(requirePlugin3.config.test).to.not.exist; }; ================================================ FILE: test/autotests/bundling/async-dependencies/a.js ================================================ a.js ================================================ FILE: test/autotests/bundling/async-dependencies/b.js ================================================ b.js ================================================ FILE: test/autotests/bundling/async-dependencies/browser.json ================================================ { "dependencies": [ "require: ./foo.js" ] } ================================================ FILE: test/autotests/bundling/async-dependencies/foo-async.js ================================================ console.log('foo-async'); require('./foo-something-else'); ================================================ FILE: test/autotests/bundling/async-dependencies/foo-something-else.js ================================================ console.log('foo-something-else'); ================================================ FILE: test/autotests/bundling/async-dependencies/foo.js ================================================ console.log('foo'); require('lasso-loader').async([ './a.js', './b.js' ], function(err) { require('./foo-async'); }); ================================================ FILE: test/autotests/bundling/async-dependencies/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/async-dependencies/test.js ================================================ var expect = require('chai').expect; var path = require('path'); exports.getLassoConfig = function() { return { fingerprintsEnabled: false }; }; exports.getInputs = function() { return [ { lassoOptions: { dependencies: [ path.join(__dirname, 'browser.json') ] }, check(lassoPageResult, writerTracker) { expect(writerTracker.getOutputFilenames()).to.deep.equal([ 'bundling-async-dependencies-async.js', 'bundling-async-dependencies.js' ]); expect(writerTracker.getCodeForFilename('bundling-async-dependencies.js')).to.contain("console.log('foo')"); expect(writerTracker.getCodeForFilename('bundling-async-dependencies.js')).to.contain('.async("'); expect(writerTracker.getCodeForFilename('bundling-async-dependencies-async.js')).to.contain("a.js"); expect(writerTracker.getCodeForFilename('bundling-async-dependencies-async.js')).to.contain("b.js"); expect(writerTracker.getCodeForFilename('bundling-async-dependencies-async.js')).to.contain("console.log('foo-async')"); expect(writerTracker.getCodeForFilename('bundling-async-dependencies-async.js')).to.contain("console.log('foo-something-else')"); } } ]; }; ================================================ FILE: test/autotests/bundling/async-flags/DO_NOT_INCLUDE_ME.js ================================================ DO_NOT_INCLUDE_ME ================================================ FILE: test/autotests/bundling/async-flags/browser.json ================================================ { "dependencies": [ "./foo.js" ], "async": { "foo": [ { "if-flag": "INVALID", "dependencies": [ "./DO_NOT_INCLUDE_ME.js" ] } ] } } ================================================ FILE: test/autotests/bundling/async-flags/foo.js ================================================ foo ================================================ FILE: test/autotests/bundling/async-flags/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/async-flags/test.js ================================================ var expect = require('chai').expect; var path = require('path'); exports.getLassoConfig = function() { return { fingerprintsEnabled: false }; }; exports.getInputs = function() { return [ { lassoOptions: { dependencies: [ path.join(__dirname, 'browser.json') ] }, check(lassoPageResult, writerTracker) { expect(writerTracker.getOutputFilenames()).to.deep.equal([ 'bundling-async-flags.js' ]); expect(writerTracker.getCodeForFilename('bundling-async-flags.js')).to.equal('foo'); } } ]; }; ================================================ FILE: test/autotests/bundling/async-package/bar.js ================================================ console.log('bar'); ================================================ FILE: test/autotests/bundling/async-package/browser.json ================================================ { "async": { "foo": [ "require: ./foo.js" ], "bar": [ "require: ./bar.js" ] } } ================================================ FILE: test/autotests/bundling/async-package/foo.js ================================================ console.log('foo'); ================================================ FILE: test/autotests/bundling/async-package/main.js ================================================ var packageId = 'foo'; console.log('main'); require('lasso-loader').async(packageId, function(err) { require('./foo'); }); var callback = function(err) { require('./bar'); }; require('lasso-loader').async('bar', callback); ================================================ FILE: test/autotests/bundling/async-package/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/async-package/test.js ================================================ var expect = require('chai').expect; exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundles: [ { name: 'foo', dependencies: [ 'require: ./foo' ] }, { name: 'bar', dependencies: [ 'require: ./bar' ] } ] }; }; exports.getInputs = function() { return [ { lassoOptions: { dependencies: [ 'require: ./main' ] }, check(lassoPageResult, writerTracker) { expect(writerTracker.getOutputFilenames()).to.deep.equal([ 'bar.js', 'bundling-async-package.js', 'foo.js' ]); expect(writerTracker.getCodeForFilename('bar.js')).to.contain("console.log('bar')"); expect(writerTracker.getCodeForFilename('bundling-async-package.js')).to.contain(".async('bar', callback)"); expect(writerTracker.getCodeForFilename('bundling-async-package.js')).to.contain("console.log('main')"); expect(writerTracker.getCodeForFilename('foo.js')).to.contain("console.log('foo')"); } } ]; }; ================================================ FILE: test/autotests/bundling/bundle-getImageInfo-skip/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/bundle-getImageInfo-skip/test.js ================================================ var expect = require('chai').expect; exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundles: [] }; }; exports.getLassoOptions = function() { return { dependencies: [ { type: 'require', path: require.resolve('../../../../taglib/helper-getImageInfo') } ] }; }; exports.check = function(lassoPageResult, writerTracker) { var fooCode = writerTracker.getCodeForFilename(writerTracker.getOutputFilenames()[0]); expect(fooCode).to.contain('lassoImage.getImageInfo'); }; ================================================ FILE: test/autotests/bundling/bundles/a.js ================================================ a ================================================ FILE: test/autotests/bundling/bundles/b.js ================================================ b ================================================ FILE: test/autotests/bundling/bundles/browser.json ================================================ { "dependencies": [ "./foo.js", "./foo.css" ] } ================================================ FILE: test/autotests/bundling/bundles/c.js ================================================ c ================================================ FILE: test/autotests/bundling/bundles/d.js ================================================ d ================================================ FILE: test/autotests/bundling/bundles/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/bundles/test.js ================================================ var expect = require('chai').expect; var path = require('path'); exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundles: [ { name: 'bundle1', dependencies: [ path.join(__dirname, 'a.js'), path.join(__dirname, 'b.js') ] }, { name: 'bundle2', dependencies: [ path.join(__dirname, 'c.js'), path.join(__dirname, 'd.js') ] } ] }; }; exports.getInputs = function() { return [ { lassoOptions: { dependencies: [ path.join(__dirname, 'a.js'), path.join(__dirname, 'b.js'), path.join(__dirname, 'c.js'), path.join(__dirname, 'd.js') ] }, check(lassoPageResult, writerTracker) { expect(writerTracker.getOutputFilenames()).to.deep.equal([ 'bundle1.js', 'bundle2.js' ]); expect(writerTracker.getCodeForFilename('bundle1.js')).to.equal('a\nb'); expect(writerTracker.getCodeForFilename('bundle2.js')).to.equal('c\nd'); } } ]; }; ================================================ FILE: test/autotests/bundling/bundling-strategy-default/a.js ================================================ a ================================================ FILE: test/autotests/bundling/bundling-strategy-default/b.js ================================================ b ================================================ FILE: test/autotests/bundling/bundling-strategy-default/browser.json ================================================ { "dependencies": [ "a.js" ] } ================================================ FILE: test/autotests/bundling/bundling-strategy-default/c.js ================================================ c ================================================ FILE: test/autotests/bundling/bundling-strategy-default/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/bundling-strategy-default/test.js ================================================ var expect = require('chai').expect; exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundles: [ { name: 'everything', dependencies: [ 'a.js', 'b.js', 'c.js' ] } ] }; }; exports.getLassoOptions = function() { return { dependencies: [ './a.js' ] }; }; exports.check = function(lassoPageResult, writerTracker) { var fooCode = writerTracker.getCodeForFilename('everything.js'); expect(fooCode).to.equal('a\n\nb\n\nc\n'); }; ================================================ FILE: test/autotests/bundling/bundling-strategy-lean/a.js ================================================ a ================================================ FILE: test/autotests/bundling/bundling-strategy-lean/b.js ================================================ b ================================================ FILE: test/autotests/bundling/bundling-strategy-lean/browser.json ================================================ { "dependencies": [ "a.js" ] } ================================================ FILE: test/autotests/bundling/bundling-strategy-lean/c.js ================================================ c ================================================ FILE: test/autotests/bundling/bundling-strategy-lean/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/bundling-strategy-lean/test.js ================================================ var expect = require('chai').expect; exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundlingStrategy: 'lean', bundles: [ { name: 'everything', dependencies: [ 'a.js', 'b.js', 'c.js' ] } ] }; }; exports.getLassoOptions = function() { return { dependencies: [ './a.js', './a.js' ] }; }; exports.check = function(lassoPageResult, writerTracker) { var everythingCode = writerTracker.getCodeForFilename('everything.js'); expect(everythingCode).to.equal('a\n'); }; ================================================ FILE: test/autotests/bundling/bundling-virtual-module/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/bundling-virtual-module/test.js ================================================ var expect = require('chai').expect; exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundlingEnabled: true }; }; exports.getLassoOptions = function() { return { dependencies: [ { type: 'require', virtualModule: { path: __dirname + '/x/y/z', clientPath: '/x/y/z', read (lassoContext) { return 'abc'; }, getDefaultBundleName: function(pageBundleName, lassoContext) { return 'xyz'; } } } ] }; }; exports.check = function(lassoPageResult, writerTracker) { var everythingCode = writerTracker.getCodeForFilename('xyz.js'); expect(everythingCode).to.contain('abc'); }; ================================================ FILE: test/autotests/bundling/csp-nonce-inline-script/a.js ================================================ a ================================================ FILE: test/autotests/bundling/csp-nonce-inline-script/b.js ================================================ b ================================================ FILE: test/autotests/bundling/csp-nonce-inline-script/browser.json ================================================ { "dependencies": [ "./foo.js", "./foo.css" ] } ================================================ FILE: test/autotests/bundling/csp-nonce-inline-script/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/csp-nonce-inline-script/test.js ================================================ var expect = require('chai').expect; var path = require('path'); exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundlingEnabled: true }; }; exports.getInputs = function() { return [ { lassoOptions: { dependencies: [ { path: path.join(__dirname, 'a.js'), inline: true }, path.join(__dirname, 'b.js') ] }, check(lassoPageResult, writerTracker) { expect(writerTracker.getOutputFilenames()).to.deep.equal([ 'bundling-csp-nonce-inline-script.js' ]); var body = lassoPageResult.getSlotHtml('body', { inlineScriptAttrs: { nonce: 'abc' } }); expect(body).to.equal( '\n' + ''); } } ]; }; ================================================ FILE: test/autotests/bundling/css-inline-resource-base64/expected.css ================================================ .foo { background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAE8AAAAeCAYAAABt5kPUAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDpGNzdGMTE3NDA3MjA2ODExODhDNkIzODA1MTg5Nzc0NiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpGRTg2MEMyMzZFNzIxMUUyQjZGMEE2ODM0MDRENkNFNiIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpGRTg2MEMyMjZFNzIxMUUyQjZGMEE2ODM0MDRENkNFNiIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IE1hY2ludG9zaCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjAyODAxMTc0MDcyMDY4MTE5OTRDOTI2RkUxMEEyMEJFIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkY3N0YxMTc0MDcyMDY4MTE4OEM2QjM4MDUxODk3NzQ2Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+Yff30gAAE/BJREFUeNrsWgdwHdW5/s+Wu7u3q3fJspqLLMuVYINt3AgQwjPFYCCN+ogpLyRgCKQHAo/H8OAFhgAOxQQCDt3E2Bg74IK7ZMuyZCFbVpescnvZet5/9kpuhJbMvJkwb2d2tPferd/5/6+cFTHDESCiAPrBZjD7+oBqOpjHjoE5MACEFwAoBbZwGekgDh2D95QJ8HthGsjUAAKpheCGYYJ3b0fibwmd5nIERPx6A67L4KRF1SlU5jhgdrkTMj0C9IcN0A0K00oUEHkCPicP51S5QBIIuPg48Byet3OVfQ/UiADoIaBmDAjnAOCdQNVjAIIXvx8CIuUCWDoQORe2awPQFjsEeDjgkXifBM4uuwPSXaVg4j5w/L45SOpBSGgBe3t04QgPCT0AezqfxedS7d84wYLAUR+0bckHt1eG1tZWEOArLCrl4JtGK+wX8+CteC64iXESeJQLxoxCVaNZI6hmn3KwScGp8AicC9wyB5YF//IL91V2ZjVocRzc7DkM1WQINBwhrLKRFcf3xIdTV1zcLgEunOKzgUOg4euwcF/1AB148BlxuNnVCg5sNevL4IBVdn6NB4rSBLtNvy7LVwaPYP0lsH2neeKwNHMYt/kvQJvCTOS4ylwJ1K8RcJ8LHmFExiMwAg9EEGxRIewzx9sUrJkAV+aFoMRpgGaRv38SBKsgU4SFEzw2xzEBEFEMHCMrjy1NyAlK+OKRI6lbJkjVTDQ4MbWyz0ghx0/2f7R8SjCIQ0SgRFQyLY1Go8X4Nx2o5eKiUdOKxUK839/NeT1dpiiZaQ4KF/sG4L9juSAR+imCJNjWy85IA7+Tg56g4R2ImGM1w8hCBXQqDi6uGdCriOSw7OASo6r+aaKlKWA4CewNI1xI9WGUVuLHvwrwLg3MRBDB7CO8sxN4WTeZvAKhKQVlQ41qyQmfw+T0lOGzFRovRXBQCIoi27ZoAp/QoDwWkMCKCQdKOH48qyoETW9sWpDcum252d19NtqWDGR3QmlKGklqnyTn9x+QamueETPTn7+owpVcbwpwsFc9rV0tmD/JA1V5jsJnPgqsaOxJXhLTrDzKxILa/Y9FTCyXxB0NJczn51a6/ifTLQTo6Q+GFYY2xWsNbbneCuy8mmoDVWhJFJtsT/ZKRFSJI6NFSJ/1TH76zNf3q70PmVogEwE0TEuTA/G236Q7x240QT+NhpjoiadYFQEHKqAdre0N1f8nY2zTUuUxOdO3e8xpdx1q2XpOfkEur6pqXLAvLOENqmp69NkXntEbDy6hoz4Cb9BuVyHl96iOF1ZV2Uwkpse7u6dr9ftvybnthisvO6N6/89f6z4xdrjh9QrJbJ940YrVfc8OB/Q0+3uejN6xbV3w9FzENMe+sSP4q43N0RtvW5h5zaKJ7nW2qLCH4SW8r77pRusjq6kaGPOpFmYrq0xbtTSJJnon6V2vPTo23PCDAjmD1qv9UySsGt2MQ33ni9rYjHkbUxenI0VtIX24kUpcp5zagR7yo8MP3RWMty0SeRceH4N8z4wXnJXzoHn80ODWbVtkrL4wsQJBMEMhX+Dun72t7ambQ1xoPjUNhMKCFqGs7GUEb5tjSu2QfviIYg0PT7IGBy/RDjYvYFXI9nNkZ/UIv71vwXVNBe1v7RpuN030ebh4ndwAMUEIhfU0t1ccnlysvJbvEz6qLZL7mvpUX+ewPqOpV7302LBeBiKxhUUUifb0NYWXXzHD9yanDwKvduWpO5ZspdGOUpswcVCJq2Q3cZWu4pxj6jhvTcIc3OgDU51mRVuW0Wh7LdtPwP2GRG/yRYciayMiZ6GpXzL5qXNLM+atZ2B+Jo9xMgxEmya/smfZdjTUskl1yPZM+Pjs8jvPzvFXmI07huGee39qA8/fe8stEHniD/+V2PDBZZzXyyoQHNOmrnZf+/3zxNKS98E0Dztqa3rx+w4hP2+3cu6iFzi3O6w3NZ/LKtIMBDyewe6K3qlz31zXoi3Hs7rZ4GKacKkJUynOk3f++zkZi8+ucK3yO/n9U4udR3DcmypypA2LJ3pWRlSrpKNfq2EAWjrlt7bE586f6FudLx4JmS0P3mn2b7uIUR4THy5nzjpH5Z0LEIuPiZTdQfy1PZDsbeP8tVuFwiuepdqxGTR6tNxCAH1WUohzkt7O8RxrL2w9TA3D+eNyL3xhlIb+3sIjTXzY+sDDvaG6qQJWPgN9TvmdNxf6pzezM7W19sLmzZtTaqs1NFYmNmz8AQICNJkER82knRmPP/odPj09ru1rAO1gEyTWbwBtbx1oBxrBCobAe+vyR5Tzzn2c7U9cLlD31J87vuXjC0GW4ycnivICueW15SXfqi6Q25DX0KpYkMSVWZaoaoFX5mLPXVN49Xm1njcAPzMAB4J63gNrB++JJC0k5LjKp1Vu5HzVa7i0ce/zuRf8EiE0wIiihiUAwo0AiS6wjq3HLhjUhMJl9yJ3W/gjwxqmyAUNHimrgwEg8Ap0BrYvwHW+iNufVXX9kQO1nxxbt1TE1tXNBBT6Z3w8IW/JGsWRjgY/KxUERvfXdu5aQqMxF3GmTijPnfP75IYP1MjK58Boa7cTgrpjFx7EYVFRUHfuAv3wYRDHj3uJvLeeVRrmRQoZ+3dcpmRNg8RxY0zhe7PTHszyCAPsuGDcBAbIyE+giBxU5TrAg4njpnMyfvdeY/TbuBsPDgKbDob+7f0ZY+89P/PS+xR/x32WHZWRf5F/rPABHBjMtyQTL60hThpYoYNgDt0OfP6SZiL6u6g2VIyKC+l6ODAlZ+Erm3vfeNCB/Ghgrt3V/sxdRf4zNhK7mSmcbJSYaNR1rrpNM6KiA3mQKeq04mt/x/MOSzNi6PXxGKIfNwYCVtNZzMuxbziP29T27e9Wt32cSw1DIpJ0mjLhbtE4oLAYjqlT41h1QbQzfosXwNXdNlP2xuQE81yWCR6vECzPcaxr6VNt0PP9AgxFDfvCLPhne3j4pF+DZvzdwXO7xuVKO5u6kmcC/hZKGNl7u6CqWJ408A0fHq8nEbQGVOpENVUHS1BxMyDe5iGcJNF4h4Atg14iScyul7FDOR+MKKcRbfdMLsp9qsFddUMk2lzGKq59ePOio8ObzyvLXLBWM6NYG4JtRQRehp5Q3fRD/e9cxarOwDbP89a+Lwm+dw4f22QDLAgcODNjqK8OSKpJ5NbBoRLbgiDKaE245JZtbxPkjNOBO1Gr6H8wVej19bgtKDBSkWI8ksMlse54rx3H0l384e5ho7tjULfH1Y65IyUv4uWC2MbAVkhVYZZX2NdE4czROHegWy25eryxBWLN843eD25HQZgDZtJzWrGMToMAkE/7fWQOwR0+GKzJv/jRD5t/+RjjM8Z39V0v3jomY+5aVg4EeUzkFFtx93W9dKuKVSehAhNMUTUFVzzmU/Lt9h0V+IpyHmbPmwLr1mwBASvHBmBEu7FyOdfxz5+d0Rhwp7olQwdiIhj8KPGScAK9nn7yJAAe1xXQAVsZtJOimtPBQbqTD6T6gdkPAsMx3Tk29oefq+1//JU1ek1r5C8vonMVYmhikyB6NKqHkAdjVsoPc0VsfEbzkxn4GCZWrXiyxT/tmmPhfbWs+tqGPvxm2+CmC8qyFr5rUbQEKAy9wb21h/rXLHXY1iQBZZkL/zox7+I16BGBiifuVUJbt2zZ5bBx/XbkZIdDBz1lHLHawvj5t9bQUBy3PxtBtALMpoyOPmdpZm9eqTshuX6GhOYe2cub7xdx1xSzMKfBRKJjWMOWoJDm5NkcYIpnUSj6wkba8XhFnLAs99Wlzr4/nh0nI8gRQUe1XcUpeW8CpxzBXh6kWiApjLnORB9omb0bDS5nlouGG/cgmKV2JbKKTPaBUwvoNUXfffj9Az9aZRsXrL69nc/9pDRj7rus8lgC2X70iTtVPSyxCuQw8s0svfEhh+CB020NRTItr6iCpVdcjKCnp/fp4fB4e+IT/aHr0ovfULfvaNX21iOYLD+eiqGtsE4nyGfNspWWR36LUgEeS7/UHx9QfgJJzc1GfDBilDkEKPA7xW5qiwpAY08Mwkn0+Aja7HI3ZLl5G0gBzfPLO0O1x8uT06DaemU+VhpnA8r2qfzxT/jsxY9ZWEkU1Ra0EO4axlGRsc58wJdeBXzmvDS98U7fyaVOmBkf2gIVZTf/ubFz1Q97w/Vn2sob3DHv6PCWxRPzlqzvDu2deqj/3UsZ1zFLk++buhZv+W+HBzb+3dQtKxJUTMHHFErHbAfDsEfJCoVkfJJ5/t/8AlxXXm6DZKcLBiDyIsu80lmzIf2Rh8B3z12K6/JLs5Urr8jK/84l6QsrHB6qG9YoB4VDhr9r2Fg8s1SBkgwHxrcktqJpT2PFNQvahzS7GnO8AnQP69Mbu5MzmViAxUG2Mx6r8ParujWSbUVnmAiu1VaojqkA7sOsr4nFhRkcK0souQ7Eqnvwt9gMTCLpp0wQsKId/IixiTGl6Hv3s4FkSmtZBuzuWLnCwHPt6Vh5i45cx47jMarVFl51v4IDIglsVtv9qZXqIkwYPw4E9Gt/SW7ecgeeTWA8lvjrez9SFi98XT5nzjBRZMBUgUoXAQ6B5HJyQCwvA0wcELrvgUcSH2y6lshy1GkklLHfvvEmIs2K0fiIWUGQVm4ZvnvJNO9a5Lm+uEZtsEbFg3FhIG5BMQL7/LbAvZaKqDGmUAksnKy+WuiNz0xEYWKKJkwReJeT2DeO1BjvRChCwPkm29aFc5Xj9iQwelb/R6pQToBHWfeGG0CPtuC15qwpSjtjE3q9c1iVoRGeu+7gil8f6l97EWtX1qLlmQvfrcg+d4thal8418NJs76xV543509WDL0TSrDRdnRC8J5frNYPNueyxMH5fMD5Uf3xL5+eBlYgAJHHn/xx4r31N2LORbUe9Atez5G6jImbaFI/4T4RvNauZMVFj7W/VdeRqHAjMCKLTogcW9lnNM6+657r+tO6fZGLbOAwomVnCh13XFByN5WK2+0gwLjLUBUabrgpVUb8iGey7EkDrEisvm5Z23/bE9bg1gWnTLLRlMiQeBD4YB3wgheml1z/a7QlNJUyKL+v+6W7DSuZxvbFTGvNLv/Rg24pB1xSJq5Zn7sKrBUxMazQWw9PNj5prSWKwpLEfLO7p06orHiK83o2oucLkFhc1CKRKr3x4PfNox2LRr0h2hxLWr78HiWtqp/bfVS2Rm4ao1iPyhPlUEdi5v0BbXd1ofIiKuo6t0wGseLcb+8Lz97fmfx+36BWDA7Ojl+EJ9oDl2XfVlsm9Rv8dU/D4A/Pt0HCgTDan/sxCews5tJmPAlGpAfMBKWJbg8mi5lWYPdNVrS3msi+IeLwBdH7laU8kQc47yTkvThIaKxVzKklaWf+DfPt6y3H1l7ClJWzyZ6AbiUBBWS1yLs3dwb2fKkZRsLenrGpKHXrtqzIUyuf1ZsPXWDPXI7MWNicx6eAYqpMR+01iwMZ6QMZV19+7Uc157/z5H4oXLNruCmpodoiwZfmSn+cN9H90ut7wn8OBfXMlEcceafBTmGOXGPEgvi8Qt/tizKv/u6stA+KfCZylAZq3fUrzK5XH7AtCjfyPMSeyzLYfB0qh8jMHLslTvaHUHkvNAO7LrH6PrzNLlJXQZNQcftk1FfdQC8XyJiFY+5EZa8/Y03DrVuw+gSWKmw/QC26cNx9Z2W4yrYxg/xlFv4XP2VEa2BmDcQdlZV/5vNyt9KkKuN32QiCk1oW5jNttMrwJuUEn59XL02pfdh54bduFGpq6o+ADyxBcvVH9HEZXqHN7xU6a4uV9zH4r67MkVa5nHw8rNOCuE6x72nqxQdWE1YazfGLrVNLlUeWzvBfO61EafTIAmS6MO1YMQQ4uZXzVmNwVbOQ6/LRJ0g2UtRiUyw8a2kipfVwaZOfEUpv/C7nGddAE+05hFg+omS3ECXvIO+b/Fe8aZNwPJjOQjbvh+ev7B6OH5nUH2mYyIyzjtGrKveCl2eW3PAo84GKmPalVmJGorZvw1gGNBwGCwkfjTOYwaDfGhgqtwYH8oRJE71G6xGNc7kGkAPbhKKCTmqYFidjCikqhsi4GoiYPLy2K2i/GTPxASuyJZYaYPfROHgVHi2KJaPCVh05po0tzXK4WvrVWJpTaKstkptVgyaZ10OgobpAgaosC3gzDEbXy1itHlsArODePJroGg+cmA1ShkCDdRHOXdVNlKJm4J1RgsEdI1rKxjBetNsdRc4/PfUeFo1wCIHiMY4l9EDWOw3LtwfjHWM5HACek5JXzXz9zBxPdT3y3z84DT/amriiSQ5ymRm7mUTKs84ElVULjh77TJnxRZNMUWBYNzGHktDZew2aAg9/163UjDHrziQKARZassAn7sMQsm9epdt+Ac7eZ3D29JXF8u1nGPLUjBwqbS8o+b1ELsB2LEXK60HDlZt6l8HaDJNCamJUZ2Q0IijoPpDnVAQohsIi4N0yq1HX9cItg9GWsRIOjIZcODZrwes+pag+ovb9c+8wTk4RwOIWtjQzxsga+AA09bIFvvpbMHtcRkBlsY2BzOyjRU9xFp+zMED0FFAmVgdF2rOYP/3suTmCALJfVTkHWIUJeO+RZG/BgZ6/3MDakymuJHgTZZnzH+gO7jnlvwn+OfD+xReClchmS2JoOZIINMdeIyBn7+p4+o6o2pfDlFbDFp+Uu/T5MRlzGnQj/s+/PfvXX5jYYKuKXoh6qzGwOFJA4t/B6CcTDvWtuZ4nDrvKZNGfQN/3hAd9nSGq/w8e4wDVX4vrNPtfQHibA3lgbdrU9+aVlqWbiujvxjThrs6/ZGWOt7qBTXT+I8v/CjAAJOOJ0FaTyhsAAAAASUVORK5CYII='); } ================================================ FILE: test/autotests/bundling/css-inline-resource-base64/foo.css ================================================ .foo { background-image: url(ebay-logo.png?base64); } ================================================ FILE: test/autotests/bundling/css-inline-resource-base64/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/css-inline-resource-base64/test.js ================================================ var expect = require('chai').expect; var path = require('path'); exports.getLassoConfig = function() { return { fingerprintsEnabled: false, includeSlotNames: true }; }; exports.getInputs = function() { return [ { lassoOptions: { dependencies: [ path.join(__dirname, 'foo.css') ] }, check(lassoPageResult, writerTracker, helpers) { expect(writerTracker.getOutputFilenames()).to.deep.equal([ 'bundling-css-inline-resource-base64-head.css' ]); var actualCSS = writerTracker.getCodeForFilename('bundling-css-inline-resource-base64-head.css'); helpers.compare(actualCSS, '.css'); } } ]; }; ================================================ FILE: test/autotests/bundling/css-inline-resource-utf8/expected.css ================================================ .foo { background-image: url('data:image/svg+xml;utf8,%0A%0A %0A%0A'); } ================================================ FILE: test/autotests/bundling/css-inline-resource-utf8/foo.css ================================================ .foo { background-image: url(./circle.svg?utf8); } ================================================ FILE: test/autotests/bundling/css-inline-resource-utf8/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/css-inline-resource-utf8/test.js ================================================ var expect = require('chai').expect; var path = require('path'); exports.getLassoConfig = function() { return { fingerprintsEnabled: false, includeSlotNames: true }; }; exports.getInputs = function() { return [ { lassoOptions: { dependencies: [ path.join(__dirname, 'foo.css') ] }, check(lassoPageResult, writerTracker, helpers) { expect(writerTracker.getOutputFilenames()).to.deep.equal([ 'bundling-css-inline-resource-utf8-head.css' ]); var actualCSS = writerTracker.getCodeForFilename('bundling-css-inline-resource-utf8-head.css'); helpers.compare(actualCSS, '.css'); } } ]; }; ================================================ FILE: test/autotests/bundling/css-resources-multiple-pages-no-bundling/foo.css ================================================ .foo { background-image: url('./foo.resource'); } ================================================ FILE: test/autotests/bundling/css-resources-multiple-pages-no-bundling/foo.resource ================================================ foo ================================================ FILE: test/autotests/bundling/css-resources-multiple-pages-no-bundling/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/css-resources-multiple-pages-no-bundling/test.js ================================================ var expect = require('chai').expect; var path = require('path'); exports.getLassoConfig = function() { return { fingerprintsEnabled: false, includeSlotNames: false, bundlingEnabled: false }; }; exports.getInputs = function() { return [ { lassoOptions: { pageName: 'css-resources-multiple-pages-no-bundling-page1', dependencies: [ path.join(__dirname, 'foo.css') ] }, check(lassoPageResult, writerTracker) { var resources = lassoPageResult.resources; var outputFiles = resources.map((resource) => { return resource.outputFile; }); expect(outputFiles[0]).to.contain('css-resources-multiple-pages-no-bundling-page1'); } }, { lassoOptions: { pageName: 'css-resources-multiple-pages-no-bundling-page2', dependencies: [ path.join(__dirname, 'foo.css') ] }, check(lassoPageResult, writerTracker) { var resources = lassoPageResult.resources; var outputFiles = resources.map((resource) => { return resource.outputFile; }); expect(outputFiles[0]).to.contain('css-resources-multiple-pages-no-bundling-page2'); } } ]; }; ================================================ FILE: test/autotests/bundling/custom-dependency-type-no-bundling/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/custom-dependency-type-no-bundling/test.js ================================================ var expect = require('chai').expect; var path = require('path'); exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundlingEnabled: false, plugins: [ { plugin: function(lasso, config) { lasso.dependencies.registerJavaScriptType('foo', { properties: { }, async init (lassoContext) {}, read: function(lassoContext) { return 'FOO'; } }); } } ] }; }; exports.getInputs = function() { return [ { lassoOptions: { dependencies: [ {type: 'foo'} ] }, check(lassoPageResult, writerTracker) { expect(writerTracker.getOutputFilenames()).to.deep.equal([ 'foo-bundling-custom-dependency-type-no-bundling.js' ]); var jsFile = lassoPageResult.getJavaScriptFiles()[0]; expect(writerTracker.getCodeForPath(jsFile)).to.equal("FOO"); } } ]; }; ================================================ FILE: test/autotests/bundling/dedupe/a.js ================================================ a ================================================ FILE: test/autotests/bundling/dedupe/b.js ================================================ b ================================================ FILE: test/autotests/bundling/dedupe/browser.json ================================================ { "dependencies": [ "./foo.js", "./foo.css" ] } ================================================ FILE: test/autotests/bundling/dedupe/c.js ================================================ c ================================================ FILE: test/autotests/bundling/dedupe/d.js ================================================ d ================================================ FILE: test/autotests/bundling/dedupe/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/dedupe/test.js ================================================ var expect = require('chai').expect; var path = require('path'); exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundles: [ { name: 'bundle1', dependencies: [ path.join(__dirname, 'a.js'), path.join(__dirname, 'b.js') ] }, { name: 'bundle2', dependencies: [ path.join(__dirname, 'b.js'), path.join(__dirname, 'c.js') ] } ] }; }; exports.getInputs = function() { return [ { lassoOptions: { dependencies: [ path.join(__dirname, 'a.js'), path.join(__dirname, 'b.js'), path.join(__dirname, 'c.js'), path.join(__dirname, 'd.js') ] }, check(lassoPageResult, writerTracker) { expect(writerTracker.getOutputFilenames()).to.deep.equal([ 'bundle1.js', 'bundle2.js', 'bundling-dedupe.js', ]); expect(writerTracker.getCodeForFilename('bundle1.js')).to.equal('a\nb'); expect(writerTracker.getCodeForFilename('bundle2.js')).to.equal('c'); expect(writerTracker.getCodeForFilename('bundling-dedupe.js')).to.equal('d'); } } ]; }; ================================================ FILE: test/autotests/bundling/dependency-code/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/dependency-code/test.js ================================================ var expect = require('chai').expect; var path = require('path'); exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundlingEnabled: false }; }; exports.getLassoOptions = function() { return { dependencies: [ { type: 'js', code: 'abc123', virtualPath: path.join(__dirname, 'virtual/foo.js') } ] }; }; exports.check = function(lassoPageResult, writerTracker) { expect(lassoPageResult.getJavaScriptUrls()[0]).to.contain('/virtual/foo.js'); var fooCode = writerTracker.getCodeForFilename(writerTracker.getOutputFilenames()[0]); expect(fooCode).to.contain('abc123'); }; ================================================ FILE: test/autotests/bundling/escape-external-css-url/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/escape-external-css-url/test.js ================================================ var expect = require('chai').expect; var path = require('path'); exports.getLassoConfig = function() { return { fingerprintsEnabled: false, includeSlotNames: true }; }; exports.getInputs = function() { return [ { lassoOptions: { dependencies: [ { type: 'css', url: 'https://fonts.googleapis.com/css?family=Open+Sans&subset=latin' } ] }, check(lassoPageResult, writerTracker) { expect(lassoPageResult.getBodyHtml()).to.equal(''); expect(lassoPageResult.getHeadHtml()).to.equal(''); } } ]; }; ================================================ FILE: test/autotests/bundling/escape-external-js-url/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/escape-external-js-url/test.js ================================================ var expect = require('chai').expect; var path = require('path'); exports.getLassoConfig = function() { return { fingerprintsEnabled: false, includeSlotNames: true }; }; exports.getInputs = function() { return [ { lassoOptions: { dependencies: [ { type: 'js', url: 'https://maps.googleapis.com/maps/api/js?key=KEY&callback=CB' } ] }, check(lassoPageResult, writerTracker) { expect(lassoPageResult.getBodyHtml()).to.equal(''); expect(lassoPageResult.getHeadHtml()).to.equal(''); } } ]; }; ================================================ FILE: test/autotests/bundling/external-js/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/external-js/test.js ================================================ var expect = require('chai').expect; var path = require('path'); exports.getLassoConfig = function() { return { fingerprintsEnabled: false, includeSlotNames: true }; }; exports.getInputs = function() { return [ { lassoOptions: { dependencies: [ 'js: http://code.jquery.com/jquery-1.11.0.min.js' ] }, check(lassoPageResult, writerTracker) { expect(lassoPageResult.getBodyHtml()).to.equal(''); expect(lassoPageResult.getHeadHtml()).to.equal(''); } } ]; }; ================================================ FILE: test/autotests/bundling/external-js-inlined/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/external-js-inlined/test.js ================================================ var expect = require('chai').expect; exports.getLassoConfig = function() { return { fingerprintsEnabled: true }; }; exports.getInputs = function() { return [ { lassoOptions: { dependencies: [ { type: 'js', url: 'http://code.jquery.com/jquery-1.11.0.min.js', external: false } ] }, check(lassoPageResult, writerTracker) { expect(lassoPageResult.getBodyHtml()).to.equal(''); expect(lassoPageResult.getHeadHtml()).to.equal(''); } } ]; }; ================================================ FILE: test/autotests/bundling/external-js-integrity/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/external-js-integrity/test.js ================================================ var expect = require('chai').expect; var path = require('path'); exports.getLassoConfig = function() { return { fingerprintsEnabled: false, includeSlotNames: true }; }; exports.getInputs = function() { return [ { lassoOptions: { dependencies: [ { type: 'js', url: 'https://maps.googleapis.com/maps/api/js?key=KEY&callback=CB', attributes: { integrity:'abc123' } } ] }, check(lassoPageResult, writerTracker) { expect(lassoPageResult.getBodyHtml()).to.equal(''); expect(lassoPageResult.getHeadHtml()).to.equal(''); } } ]; }; ================================================ FILE: test/autotests/bundling/fingerprints/browser.json ================================================ { "dependencies": [ "./foo.js", "./foo.css" ] } ================================================ FILE: test/autotests/bundling/fingerprints/foo.css ================================================ .foo { color: red; } ================================================ FILE: test/autotests/bundling/fingerprints/foo.js ================================================ console.log('foo'); ================================================ FILE: test/autotests/bundling/fingerprints/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/fingerprints/test.js ================================================ var expect = require('chai').expect; var path = require('path'); exports.getLassoConfig = function() { return { fingerprintsEnabled: true }; }; exports.getInputs = function() { return [ { lassoOptions: { dependencies: [ path.join(__dirname, 'browser.json') ] }, check(lassoPageResult, writerTracker) { expect(writerTracker.getOutputFilenames()).to.deep.equal([ 'bundling-fingerprints-15c73efd.css', 'bundling-fingerprints-b691c0e8.js' ]); } } ]; }; ================================================ FILE: test/autotests/bundling/glob-patterns/browser.json ================================================ { "dependencies": [ "style/*.css", "require: require/*.js" ] } ================================================ FILE: test/autotests/bundling/glob-patterns/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/glob-patterns/require/bar.js ================================================ module.exports="BAR"; ================================================ FILE: test/autotests/bundling/glob-patterns/require/foo.js ================================================ module.exports="FOO"; ================================================ FILE: test/autotests/bundling/glob-patterns/style/style1.css ================================================ .style1 {} ================================================ FILE: test/autotests/bundling/glob-patterns/style/style2.css ================================================ .style2 {} ================================================ FILE: test/autotests/bundling/glob-patterns/test.js ================================================ var expect = require('chai').expect; var path = require('path'); exports.getLassoConfig = function() { return { fingerprintsEnabled: false }; }; exports.getInputs = function() { return [ { lassoOptions: { dependencies: [ path.join(__dirname, 'browser.json') ] }, check(lassoPageResult, writerTracker) { expect(writerTracker.getOutputFilenames()).to.deep.equal([ 'bundling-glob-patterns.css', 'bundling-glob-patterns.js' ]); var jsFile = lassoPageResult.getJavaScriptFiles()[0]; var cssFile = lassoPageResult.getCSSFiles()[0]; expect(writerTracker.getCodeForPath(cssFile)).to.equal(".style1 {}\n.style2 {}"); expect(writerTracker.getCodeForPath(jsFile)).to.contain("FOO"); expect(writerTracker.getCodeForPath(jsFile)).to.contain("BAR"); expect(writerTracker.getCodeForPath(jsFile)).to.contain("$_mod.def"); } } ]; }; ================================================ FILE: test/autotests/bundling/glob-patterns-relative-paths/browser.json ================================================ { "dependencies": [ "./style/*.css", "require: ./require/*.js" ] } ================================================ FILE: test/autotests/bundling/glob-patterns-relative-paths/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/glob-patterns-relative-paths/require/bar.js ================================================ module.exports="BAR"; ================================================ FILE: test/autotests/bundling/glob-patterns-relative-paths/require/foo.js ================================================ module.exports="FOO"; ================================================ FILE: test/autotests/bundling/glob-patterns-relative-paths/style/style1.css ================================================ .style1 {} ================================================ FILE: test/autotests/bundling/glob-patterns-relative-paths/style/style2.css ================================================ .style2 {} ================================================ FILE: test/autotests/bundling/glob-patterns-relative-paths/test.js ================================================ var expect = require('chai').expect; var path = require('path'); exports.getLassoConfig = function() { return { fingerprintsEnabled: false }; }; exports.getInputs = function() { return [ { lassoOptions: { dependencies: [ path.join(__dirname, 'browser.json') ] }, check(lassoPageResult, writerTracker) { expect(writerTracker.getOutputFilenames()).to.deep.equal([ 'bundling-glob-patterns-relative-paths.css', 'bundling-glob-patterns-relative-paths.js' ]); var jsFile = lassoPageResult.getJavaScriptFiles()[0]; var cssFile = lassoPageResult.getCSSFiles()[0]; expect(writerTracker.getCodeForPath(cssFile)).to.equal(".style1 {}\n.style2 {}"); expect(writerTracker.getCodeForPath(jsFile)).to.contain("FOO"); expect(writerTracker.getCodeForPath(jsFile)).to.contain("BAR"); expect(writerTracker.getCodeForPath(jsFile)).to.contain("$_mod.def"); } } ]; }; ================================================ FILE: test/autotests/bundling/inline-async-script/a.js ================================================ var a; ================================================ FILE: test/autotests/bundling/inline-async-script/b.js ================================================ var b; ================================================ FILE: test/autotests/bundling/inline-async-script/browser.json ================================================ { "dependencies": [ "./foo.js" ] } ================================================ FILE: test/autotests/bundling/inline-async-script/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/inline-async-script/test.js ================================================ var expect = require('chai').expect; var path = require('path'); exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundlingEnabled: true }; }; exports.getInputs = function() { return [ { lassoOptions: { dependencies: [ { path: path.join(__dirname, 'a.js'), inline: true }, path.join(__dirname, 'b.js') ] }, check(lassoPageResult, writerTracker) { expect(writerTracker.getOutputFilenames()).to.deep.equal([ 'bundling-inline-async-script.js' ]); var body = lassoPageResult.getSlotHtml('body', { externalScriptAttrs: { async: true } }); expect(body).to.equal( '\n' + ''); } } ]; }; ================================================ FILE: test/autotests/bundling/inline-css-html-chars/expected.html ================================================ ================================================ FILE: test/autotests/bundling/inline-css-html-chars/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/inline-css-html-chars/style.css ================================================ .foo { background-image: url("data:image/svg+xml;utf8,") } ================================================ FILE: test/autotests/bundling/inline-css-html-chars/test.js ================================================ var expect = require('chai').expect; var path = require('path'); exports.getLassoConfig = function() { return { fingerprintsEnabled: false, includeSlotNames: true }; }; exports.getInputs = function() { return [ { lassoOptions: { dependencies: [ { path: path.join(__dirname, 'style.css'), inline: true } ] }, check(lassoPageResult, writerTracker, helpers) { var head = lassoPageResult.getSlotHtml('head'); helpers.compare(head, '.html'); } } ]; }; ================================================ FILE: test/autotests/bundling/inline-defer-script/a.js ================================================ var a; ================================================ FILE: test/autotests/bundling/inline-defer-script/b.js ================================================ var b; ================================================ FILE: test/autotests/bundling/inline-defer-script/browser.json ================================================ { "dependencies": [ "./foo.js" ] } ================================================ FILE: test/autotests/bundling/inline-defer-script/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/inline-defer-script/test.js ================================================ var expect = require('chai').expect; var path = require('path'); exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundlingEnabled: true }; }; exports.getInputs = function() { return [ { lassoOptions: { dependencies: [ { path: path.join(__dirname, 'a.js'), inline: true }, path.join(__dirname, 'b.js') ] }, check(lassoPageResult, writerTracker) { expect(writerTracker.getOutputFilenames()).to.deep.equal([ 'bundling-inline-defer-script.js' ]); var body = lassoPageResult.getSlotHtml('body', { externalScriptAttrs: { defer: true } }); expect(body).to.equal( '\n' + ''); } } ]; }; ================================================ FILE: test/autotests/bundling/inline-script-external-attrs/a.js ================================================ var a; ================================================ FILE: test/autotests/bundling/inline-script-external-attrs/b.js ================================================ var b; ================================================ FILE: test/autotests/bundling/inline-script-external-attrs/browser.json ================================================ { "dependencies": [ "./foo.js" ] } ================================================ FILE: test/autotests/bundling/inline-script-external-attrs/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/inline-script-external-attrs/test.js ================================================ var expect = require('chai').expect; var path = require('path'); exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundlingEnabled: true }; }; exports.getInputs = function() { return [ { lassoOptions: { dependencies: [ { path: path.join(__dirname, 'a.js'), inline: true }, path.join(__dirname, 'b.js') ] }, check(lassoPageResult, writerTracker) { expect(writerTracker.getOutputFilenames()).to.deep.equal([ 'bundling-inline-script-external-attrs.js' ]); var body = lassoPageResult.getSlotHtml('body', { externalScriptAttrs: { x: 1 } }); expect(body).to.equal( '\n' + '' ); } } ]; }; ================================================ FILE: test/autotests/bundling/intersection/bar.js ================================================ BAR ================================================ FILE: test/autotests/bundling/intersection/common.js ================================================ COMMON ================================================ FILE: test/autotests/bundling/intersection/foo.js ================================================ FOO ================================================ FILE: test/autotests/bundling/intersection/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/intersection/page1.browser.json ================================================ { "dependencies": [ "foo.js", "common.js" ] } ================================================ FILE: test/autotests/bundling/intersection/page2.browser.json ================================================ { "dependencies": [ "common.js", "bar.js" ] } ================================================ FILE: test/autotests/bundling/intersection/test.js ================================================ var expect = require('chai').expect; var path = require('path'); exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundles: [ { name: 'common', dependencies: [ { 'intersection': [ path.join(__dirname, 'page1.browser.json'), path.join(__dirname, 'page2.browser.json') ] } ] } ] }; }; exports.getInputs = function() { return [ { lassoOptions: { pageName: 'page1', dependencies: [ path.join(__dirname, 'page1.browser.json') ] }, check(lassoPageResult, writerTracker) { expect(writerTracker.getCodeForFilename('page1.js')).to.equal("FOO"); expect(writerTracker.getOutputFilenames()).to.deep.equal([ 'common.js', 'page1.js' ]); expect(writerTracker.getCodeForFilename('common.js')).to.equal("COMMON"); } }, { lassoOptions: { pageName: 'page2', dependencies: [ path.join(__dirname, 'page2.browser.json') ] }, check(lassoPageResult, writerTracker) { expect(writerTracker.getCodeForFilename('page2.js')).to.equal("BAR"); expect(writerTracker.getOutputFilenames()).to.deep.equal([ 'common.js', 'page2.js' ]); expect(writerTracker.getCodeForFilename('common.js')).to.equal("COMMON"); } } ]; }; ================================================ FILE: test/autotests/bundling/intersection-threshold-100percent/a.browser.json ================================================ { "dependencies": [ "./a.js" ] } ================================================ FILE: test/autotests/bundling/intersection-threshold-100percent/a.js ================================================ a=true; ================================================ FILE: test/autotests/bundling/intersection-threshold-100percent/ab.browser.json ================================================ { "dependencies": [ "./a.js", "./b.js" ] } ================================================ FILE: test/autotests/bundling/intersection-threshold-100percent/abc.browser.json ================================================ { "dependencies": [ "./a.js", "./b.js", "./c.js" ] } ================================================ FILE: test/autotests/bundling/intersection-threshold-100percent/b.js ================================================ b=true; ================================================ FILE: test/autotests/bundling/intersection-threshold-100percent/bc.browser.json ================================================ { "dependencies": [ "./b.js", "./c.js" ] } ================================================ FILE: test/autotests/bundling/intersection-threshold-100percent/c.js ================================================ c=true; ================================================ FILE: test/autotests/bundling/intersection-threshold-100percent/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/intersection-threshold-100percent/test.js ================================================ var expect = require('chai').expect; var path = require('path'); exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundles: [ { name: 'common', dependencies: [ { threshold: '100%', intersection: [ // a.js is found in 2/3 (100%) // b.js is found in 2/3 (66.6%) // c.js if found in 1/3 (33.3%) path.join(__dirname, 'a.browser.json'), path.join(__dirname, 'ab.browser.json'), path.join(__dirname, 'abc.browser.json') ] } ] } ] }; }; exports.getInputs = function() { return [ { lassoOptions: { dependencies: [ path.join(__dirname, 'abc.browser.json') ] }, check(lassoPageResult, writerTracker) { expect(writerTracker.getOutputFilenames()).to.deep.equal([ 'bundling-intersection-threshold-100percent.js', 'common.js' ]); expect(writerTracker.getCodeForFilename('common.js')).to.equal('a=true;'); expect(writerTracker.getCodeForFilename('bundling-intersection-threshold-100percent.js')).to.equal('b=true;\nc=true;'); } } ]; }; ================================================ FILE: test/autotests/bundling/intersection-threshold-2/a.browser.json ================================================ { "dependencies": [ "./a.js" ] } ================================================ FILE: test/autotests/bundling/intersection-threshold-2/a.js ================================================ a=true; ================================================ FILE: test/autotests/bundling/intersection-threshold-2/ab.browser.json ================================================ { "dependencies": [ "./a.js", "./b.js" ] } ================================================ FILE: test/autotests/bundling/intersection-threshold-2/abc.browser.json ================================================ { "dependencies": [ "./a.js", "./b.js", "./c.js" ] } ================================================ FILE: test/autotests/bundling/intersection-threshold-2/b.js ================================================ b=true; ================================================ FILE: test/autotests/bundling/intersection-threshold-2/bc.browser.json ================================================ { "dependencies": [ "./b.js", "./c.js" ] } ================================================ FILE: test/autotests/bundling/intersection-threshold-2/c.js ================================================ c=true; ================================================ FILE: test/autotests/bundling/intersection-threshold-2/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/intersection-threshold-2/test.js ================================================ var expect = require('chai').expect; var path = require('path'); exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundles: [ { name: 'common', dependencies: [ { threshold: 2, intersection: [ // a.js is found in 2/3 (100%) // b.js is found in 2/3 (66.6%) // c.js if found in 1/3 (33.3%) path.join(__dirname, 'a.browser.json'), path.join(__dirname, 'ab.browser.json'), path.join(__dirname, 'abc.browser.json') ] } ] } ] }; }; exports.getInputs = function() { return [ { lassoOptions: { dependencies: [ path.join(__dirname, 'abc.browser.json') ] }, check(lassoPageResult, writerTracker) { expect(writerTracker.getOutputFilenames()).to.deep.equal([ 'bundling-intersection-threshold-2.js', 'common.js' ]); expect(writerTracker.getCodeForFilename('common.js')).to.equal('a=true;\nb=true;'); expect(writerTracker.getCodeForFilename('bundling-intersection-threshold-2.js')).to.equal('c=true;'); } } ]; }; ================================================ FILE: test/autotests/bundling/intersection-threshold-2-require/a.js ================================================ 'THIS_IS_A' require('./shared'); ================================================ FILE: test/autotests/bundling/intersection-threshold-2-require/b.js ================================================ 'THIS_IS_B' require('./shared'); ================================================ FILE: test/autotests/bundling/intersection-threshold-2-require/main.browser.json ================================================ { "dependencies": [ "require: ./a", "require: ./b" ] } ================================================ FILE: test/autotests/bundling/intersection-threshold-2-require/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/intersection-threshold-2-require/shared.js ================================================ 'THIS_IS_SHARED' ================================================ FILE: test/autotests/bundling/intersection-threshold-2-require/test.js ================================================ var expect = require('chai').expect; var path = require('path'); exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundles: [ { name: 'common', dependencies: [ { intersection: [ 'require: ./a', 'require: ./b' ] } ] } ] }; }; exports.getInputs = function() { return [ { lassoOptions: { dependencies: [ path.join(__dirname, 'main.browser.json') ] }, check(lassoPageResult, writerTracker) { expect(writerTracker.getOutputFilenames()).to.deep.equal([ 'bundling-intersection-threshold-2-require.js', 'common.js' ]); expect(writerTracker.getCodeForFilename('common.js')).to.contain('THIS_IS_SHARED'); expect(writerTracker.getCodeForFilename('bundling-intersection-threshold-2-require.js')).to.contain('THIS_IS_A'); expect(writerTracker.getCodeForFilename('bundling-intersection-threshold-2-require.js')).to.contain('THIS_IS_B'); } } ]; }; ================================================ FILE: test/autotests/bundling/intersection-threshold-2str/a.browser.json ================================================ { "dependencies": [ "./a.js" ] } ================================================ FILE: test/autotests/bundling/intersection-threshold-2str/a.js ================================================ a=true; ================================================ FILE: test/autotests/bundling/intersection-threshold-2str/ab.browser.json ================================================ { "dependencies": [ "./a.js", "./b.js" ] } ================================================ FILE: test/autotests/bundling/intersection-threshold-2str/abc.browser.json ================================================ { "dependencies": [ "./a.js", "./b.js", "./c.js" ] } ================================================ FILE: test/autotests/bundling/intersection-threshold-2str/b.js ================================================ b=true; ================================================ FILE: test/autotests/bundling/intersection-threshold-2str/bc.browser.json ================================================ { "dependencies": [ "./b.js", "./c.js" ] } ================================================ FILE: test/autotests/bundling/intersection-threshold-2str/c.js ================================================ c=true; ================================================ FILE: test/autotests/bundling/intersection-threshold-2str/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/intersection-threshold-2str/test.js ================================================ var expect = require('chai').expect; var path = require('path'); exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundles: [ { name: 'common', dependencies: [ { threshold: '2', intersection: [ // a.js is found in 2/3 (100%) // b.js is found in 2/3 (66.6%) // c.js if found in 1/3 (33.3%) path.join(__dirname, 'a.browser.json'), path.join(__dirname, 'ab.browser.json'), path.join(__dirname, 'abc.browser.json') ] } ] } ] }; }; exports.getInputs = function() { return [ { lassoOptions: { dependencies: [ path.join(__dirname, 'abc.browser.json') ] }, check(lassoPageResult, writerTracker) { expect(writerTracker.getOutputFilenames()).to.deep.equal([ 'bundling-intersection-threshold-2str.js', 'common.js' ]); expect(writerTracker.getCodeForFilename('common.js')).to.equal('a=true;\nb=true;'); expect(writerTracker.getCodeForFilename('bundling-intersection-threshold-2str.js')).to.equal('c=true;'); } } ]; }; ================================================ FILE: test/autotests/bundling/intersection-threshold-3/a.browser.json ================================================ { "dependencies": [ "./a.js" ] } ================================================ FILE: test/autotests/bundling/intersection-threshold-3/a.js ================================================ a=true; ================================================ FILE: test/autotests/bundling/intersection-threshold-3/ab.browser.json ================================================ { "dependencies": [ "./a.js", "./b.js" ] } ================================================ FILE: test/autotests/bundling/intersection-threshold-3/abc.browser.json ================================================ { "dependencies": [ "./a.js", "./b.js", "./c.js" ] } ================================================ FILE: test/autotests/bundling/intersection-threshold-3/b.js ================================================ b=true; ================================================ FILE: test/autotests/bundling/intersection-threshold-3/bc.browser.json ================================================ { "dependencies": [ "./b.js", "./c.js" ] } ================================================ FILE: test/autotests/bundling/intersection-threshold-3/c.js ================================================ c=true; ================================================ FILE: test/autotests/bundling/intersection-threshold-3/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/intersection-threshold-3/test.js ================================================ var expect = require('chai').expect; var path = require('path'); exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundles: [ { name: 'common', dependencies: [ { threshold: 3, intersection: [ // a.js is found in 2/3 (100%) // b.js is found in 2/3 (66.6%) // c.js if found in 1/3 (33.3%) path.join(__dirname, 'a.browser.json'), path.join(__dirname, 'ab.browser.json'), path.join(__dirname, 'abc.browser.json') ] } ] } ] }; }; exports.getInputs = function() { return [ { lassoOptions: { dependencies: [ path.join(__dirname, 'abc.browser.json') ] }, check(lassoPageResult, writerTracker) { expect(writerTracker.getOutputFilenames()).to.deep.equal([ 'bundling-intersection-threshold-3.js', 'common.js' ]); expect(writerTracker.getCodeForFilename('common.js')).to.equal('a=true;'); expect(writerTracker.getCodeForFilename('bundling-intersection-threshold-3.js')).to.equal('b=true;\nc=true;'); } } ]; }; ================================================ FILE: test/autotests/bundling/intersection-threshold-50percent/a.browser.json ================================================ { "dependencies": [ "./a.js" ] } ================================================ FILE: test/autotests/bundling/intersection-threshold-50percent/a.js ================================================ a=true; ================================================ FILE: test/autotests/bundling/intersection-threshold-50percent/ab.browser.json ================================================ { "dependencies": [ "./a.js", "./b.js" ] } ================================================ FILE: test/autotests/bundling/intersection-threshold-50percent/abc.browser.json ================================================ { "dependencies": [ "./a.js", "./b.js", "./c.js" ] } ================================================ FILE: test/autotests/bundling/intersection-threshold-50percent/b.js ================================================ b=true; ================================================ FILE: test/autotests/bundling/intersection-threshold-50percent/bc.browser.json ================================================ { "dependencies": [ "./b.js", "./c.js" ] } ================================================ FILE: test/autotests/bundling/intersection-threshold-50percent/c.js ================================================ c=true; ================================================ FILE: test/autotests/bundling/intersection-threshold-50percent/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/intersection-threshold-50percent/test.js ================================================ var expect = require('chai').expect; var path = require('path'); exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundles: [ { name: 'common', dependencies: [ { threshold: '50%', intersection: [ // a.js is found in 2/3 (100%) // b.js is found in 2/3 (66.6%) // c.js if found in 1/3 (33.3%) path.join(__dirname, 'a.browser.json'), path.join(__dirname, 'ab.browser.json'), path.join(__dirname, 'abc.browser.json') ] } ] } ] }; }; exports.getInputs = function() { return [ { lassoOptions: { dependencies: [ path.join(__dirname, 'abc.browser.json') ] }, check(lassoPageResult, writerTracker) { expect(writerTracker.getOutputFilenames()).to.deep.equal([ 'bundling-intersection-threshold-50percent.js', 'common.js' ]); expect(writerTracker.getCodeForFilename('common.js')).to.equal('a=true;\nb=true;'); expect(writerTracker.getCodeForFilename('bundling-intersection-threshold-50percent.js')).to.equal('c=true;'); } } ]; }; ================================================ FILE: test/autotests/bundling/modules-ready-lastSlot/body.js ================================================ console.log('body'); ================================================ FILE: test/autotests/bundling/modules-ready-lastSlot/head.js ================================================ console.log('head'); ================================================ FILE: test/autotests/bundling/modules-ready-lastSlot/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/modules-ready-lastSlot/test.js ================================================ var expect = require('chai').expect; var path = require('path'); exports.getLassoConfig = function() { return { fingerprintsEnabled: false, require: { lastSlot: 'body' } }; }; exports.getInputs = function() { return [ { lassoOptions: { dependencies: [ { type: 'require', path: path.join(__dirname, 'head.js'), run: true, wait: false, slot: 'head' }, { type: 'require', path: path.join(__dirname, 'body.js'), run: true, wait: false } ] }, check(lassoPageResult, writerTracker) { var htmlBySlot = lassoPageResult.getHtmlBySlot(); expect(htmlBySlot.head).to.equal(''); expect(htmlBySlot.body).to.equal('\n'); } } ]; }; ================================================ FILE: test/autotests/bundling/no-bundles/main.js ================================================ require('foo'); console.log('[MAIN]'); ================================================ FILE: test/autotests/bundling/no-bundles/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/no-bundles/test.js ================================================ var expect = require('chai').expect; exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundles: [] }; }; exports.getLassoOptions = function() { return { dependencies: [ 'require: ./main' ] }; }; exports.check = function(lassoPageResult, writerTracker) { var fooCode = writerTracker.getCodeForFilename(writerTracker.getOutputFilenames()[0]); expect(fooCode).to.contain('[MAIN]'); expect(fooCode).to.contain('[FOO]'); expect(fooCode).to.contain('[FOO_INDEX]'); expect(fooCode).to.contain('[BAR]'); expect(fooCode).to.contain('[BAZ]'); }; ================================================ FILE: test/autotests/bundling/recurseInto-all/main.js ================================================ require('foo'); console.log('[MAIN]'); ================================================ FILE: test/autotests/bundling/recurseInto-all/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/recurseInto-all/test.js ================================================ var expect = require('chai').expect; exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundles: [ { name: 'foo', dependencies: [ // Specified for a single dependency: { path: 'require: foo', recurseInto: 'all' } ] } ] }; }; exports.getLassoOptions = function() { return { dependencies: [ 'require: ./main' ] }; }; exports.check = function(lassoPageResult, writerTracker) { var fooCode = writerTracker.getCodeForFilename('foo.js'); expect(fooCode).to.not.contain('[MAIN]'); expect(fooCode).to.contain('[FOO]'); expect(fooCode).to.contain('[FOO_INDEX]'); expect(fooCode).to.contain('[BAR]'); expect(fooCode).to.contain('[BAZ]'); }; ================================================ FILE: test/autotests/bundling/recurseInto-dir/main.js ================================================ require('foo'); console.log('[MAIN]'); ================================================ FILE: test/autotests/bundling/recurseInto-dir/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/recurseInto-dir/test.js ================================================ var expect = require('chai').expect; exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundles: [ { name: 'foo', dependencies: [ // Specified for a single dependency: { path: 'require: foo', recurseInto: 'dir' } ] } ] }; }; exports.getLassoOptions = function() { return { dependencies: [ 'require: ./main' ] }; }; exports.check = function(lassoPageResult, writerTracker) { var fooCode = writerTracker.getCodeForFilename('foo.js'); expect(fooCode).to.not.contain('[MAIN]'); expect(fooCode).to.not.contain('[FOO]'); expect(fooCode).to.contain('[FOO_INDEX]'); expect(fooCode).to.not.contain('[BAR]'); expect(fooCode).to.not.contain('[BAZ]'); }; ================================================ FILE: test/autotests/bundling/recurseInto-dirtree/main.js ================================================ require('foo'); console.log('[MAIN]'); ================================================ FILE: test/autotests/bundling/recurseInto-dirtree/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/recurseInto-dirtree/test.js ================================================ var expect = require('chai').expect; exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundles: [ { name: 'foo', dependencies: [ // Specified for a single dependency: { path: 'require: foo', recurseInto: 'dirtree' } ] } ] }; }; exports.getLassoOptions = function() { return { dependencies: [ 'require: ./main' ] }; }; exports.check = function(lassoPageResult, writerTracker) { var fooCode = writerTracker.getCodeForFilename('foo.js'); expect(fooCode).to.not.contain('[MAIN]'); expect(fooCode).to.contain('[FOO]'); expect(fooCode).to.contain('[FOO_INDEX]'); expect(fooCode).to.contain('[BAR]'); expect(fooCode).to.not.contain('[BAZ]'); }; ================================================ FILE: test/autotests/bundling/recurseInto-module/main.js ================================================ require('foo'); console.log('[MAIN]'); ================================================ FILE: test/autotests/bundling/recurseInto-module/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/recurseInto-module/test.js ================================================ var expect = require('chai').expect; exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundles: [ { name: 'foo', dependencies: [ // Specified for a single dependency: { path: 'require: foo', recurseInto: 'module' } ] } ] }; }; exports.getLassoOptions = function() { return { dependencies: [ 'require: ./main' ] }; }; exports.check = function(lassoPageResult, writerTracker) { var fooCode = writerTracker.getCodeForFilename('foo.js'); expect(fooCode).to.not.contain('[MAIN]'); expect(fooCode).to.contain('[FOO]'); expect(fooCode).to.contain('[FOO_INDEX]'); expect(fooCode).to.not.contain('[BAR]'); expect(fooCode).to.not.contain('[BAZ]'); }; ================================================ FILE: test/autotests/bundling/registerRequireExtension-not-cacheable/foo.js ================================================ $$FOO$$ ================================================ FILE: test/autotests/bundling/registerRequireExtension-not-cacheable/hello.dynamic ================================================ ================================================ FILE: test/autotests/bundling/registerRequireExtension-not-cacheable/hello.js ================================================ $$HELLO$$ ================================================ FILE: test/autotests/bundling/registerRequireExtension-not-cacheable/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/registerRequireExtension-not-cacheable/test.js ================================================ var expect = require('chai').expect; var path = require('path'); exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundlingEnabled: true, plugins: [ function(lasso, pluginConfig) { lasso.dependencies.registerRequireExtension( 'dynamic', { read: function(filename, lassoContext) { var target = lassoContext.data.target; return 'module.exports = require("./' + target + '")'; } }); } ] }; }; exports.getInputs = function() { return [ { lassoOptions: { flags: ['hello'], data: { target: 'hello' }, dependencies: [ `require: ${path.join(__dirname, 'hello.dynamic')}` ] }, check(lassoPageResult, writerTracker) { expect(writerTracker.getOutputFilenames().length).to.equal(1); expect(writerTracker.getCodeForFilename(writerTracker.getOutputFilenames()[0])).to.contain('$$HELLO$$'); expect(writerTracker.getCodeForFilename(writerTracker.getOutputFilenames()[0])).to.not.contain('$$FOO$$'); } }, { lassoOptions: { flags: ['foo'], data: { target: 'foo' }, dependencies: [ `require: ${path.join(__dirname, 'hello.dynamic')}` ] }, check(lassoPageResult, writerTracker) { expect(writerTracker.getOutputFilenames().length).to.equal(1); expect(writerTracker.getCodeForFilename(writerTracker.getOutputFilenames()[0])).to.contain('$$FOO$$'); expect(writerTracker.getCodeForFilename(writerTracker.getOutputFilenames()[0])).to.not.contain('$$HELLO$$'); } } ]; }; ================================================ FILE: test/autotests/bundling/resource-absolute-path/foo.js ================================================ console.log('MAIN'); ================================================ FILE: test/autotests/bundling/resource-absolute-path/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/resource-absolute-path/test.js ================================================ var expect = require('chai').expect; var path = require('path'); exports.getLassoConfig = function() { return { fingerprintsEnabled: false }; }; exports.getInputs = function() { return [ { lassoOptions: { dependencies: [ path.join(__dirname, 'foo.js') ] }, check(lassoPageResult, writerTracker) { expect(writerTracker.getOutputFilenames()).to.deep.equal([ 'bundling-resource-absolute-path.js' ]); expect(writerTracker.getCodeForFilename('bundling-resource-absolute-path.js')).to.equal("console.log('MAIN');\n"); } } ]; }; ================================================ FILE: test/autotests/bundling/slots/foo.css ================================================ foo_css ================================================ FILE: test/autotests/bundling/slots/foo.js ================================================ foo_js ================================================ FILE: test/autotests/bundling/slots/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/slots/test.js ================================================ var expect = require('chai').expect; var path = require('path'); exports.getLassoConfig = function() { return { fingerprintsEnabled: false, includeSlotNames: true }; }; exports.getInputs = function() { return [ { lassoOptions: { dependencies: [ path.join(__dirname, 'foo.js'), path.join(__dirname, 'foo.css') ] }, check(lassoPageResult, writerTracker) { expect(writerTracker.getOutputFilenames()).to.deep.equal([ 'bundling-slots-body.js', 'bundling-slots-head.css' ]); expect(writerTracker.getCodeForFilename('bundling-slots-head.css')).to.equal('foo_css'); expect(writerTracker.getCodeForFilename('bundling-slots-body.js')).to.equal('foo_js'); } } ]; }; ================================================ FILE: test/autotests/bundling/slots-inline-beginning/a.js ================================================ a ================================================ FILE: test/autotests/bundling/slots-inline-beginning/b.js ================================================ b ================================================ FILE: test/autotests/bundling/slots-inline-beginning/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/slots-inline-beginning/test.js ================================================ var expect = require('chai').expect; var path = require('path'); exports.getLassoConfig = function() { return { fingerprintsEnabled: false, includeSlotNames: true }; }; exports.getInputs = function() { return [ { lassoOptions: { dependencies: [ { path: path.join(__dirname, 'a.js'), inline: 'beginning' }, path.join(__dirname, 'b.js') ] }, check(lassoPageResult, writerTracker) { expect(writerTracker.getOutputFilenames()).to.deep.equal(['bundling-slots-inline-beginning-body.js']); var body = lassoPageResult.getSlotHtml('body'); expect(body).to.equal('\n'); } } ]; }; ================================================ FILE: test/autotests/bundling/slots-inline-end/a.js ================================================ a ================================================ FILE: test/autotests/bundling/slots-inline-end/b.js ================================================ b ================================================ FILE: test/autotests/bundling/slots-inline-end/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/slots-inline-end/test.js ================================================ var expect = require('chai').expect; var path = require('path'); exports.getLassoConfig = function() { return { fingerprintsEnabled: false, includeSlotNames: true }; }; exports.getInputs = function() { return [ { lassoOptions: { dependencies: [ { path: path.join(__dirname, 'a.js'), inline: 'end' }, path.join(__dirname, 'b.js') ] }, check(lassoPageResult, writerTracker) { expect(writerTracker.getOutputFilenames()).to.deep.equal(['bundling-slots-inline-end-body.js']); var body = lassoPageResult.getSlotHtml('body'); expect(body).to.equal('\n'); } } ]; }; ================================================ FILE: test/autotests/bundling/slots-inline-false/a.js ================================================ a ================================================ FILE: test/autotests/bundling/slots-inline-false/b.js ================================================ b ================================================ FILE: test/autotests/bundling/slots-inline-false/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/slots-inline-false/test.js ================================================ var expect = require('chai').expect; var path = require('path'); exports.getLassoConfig = function() { return { fingerprintsEnabled: false, includeSlotNames: true }; }; exports.getInputs = function() { return [ { lassoOptions: { dependencies: [ { path: path.join(__dirname, 'a.js'), inline: false }, path.join(__dirname, 'b.js') ] }, check(lassoPageResult, writerTracker) { expect(writerTracker.getOutputFilenames()).to.deep.equal(['bundling-slots-inline-false-body.js']); var body = lassoPageResult.getSlotHtml('body'); expect(body).to.equal(''); } } ]; }; ================================================ FILE: test/autotests/bundling/slots-inline-in-place/a.js ================================================ a ================================================ FILE: test/autotests/bundling/slots-inline-in-place/b.js ================================================ b ================================================ FILE: test/autotests/bundling/slots-inline-in-place/c.js ================================================ c ================================================ FILE: test/autotests/bundling/slots-inline-in-place/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/slots-inline-in-place/test.js ================================================ var expect = require('chai').expect; var path = require('path'); exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundlingEnabled: false }; }; exports.getInputs = function() { return [ { lassoOptions: { dependencies: [ path.join(__dirname, 'a.js'), { path: path.join(__dirname, 'b.js'), inline: 'in-place' }, path.join(__dirname, 'c.js') ] }, check(lassoPageResult, writerTracker) { expect(writerTracker.getOutputFilenames()).to.deep.equal([ 'a.js', 'c.js' ]); var body = lassoPageResult.getSlotHtml('body'); expect(body).to.equal( '\n' + '\n' + ''); } } ]; }; ================================================ FILE: test/autotests/bundling/slots-inline-true/a.js ================================================ a ================================================ FILE: test/autotests/bundling/slots-inline-true/b.js ================================================ b ================================================ FILE: test/autotests/bundling/slots-inline-true/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/slots-inline-true/test.js ================================================ var expect = require('chai').expect; var path = require('path'); exports.getLassoConfig = function() { return { fingerprintsEnabled: false, includeSlotNames: true }; }; exports.getInputs = function() { return [ { lassoOptions: { dependencies: [ { path: path.join(__dirname, 'a.js'), inline: true }, path.join(__dirname, 'b.js') ] }, check(lassoPageResult, writerTracker) { expect(writerTracker.getOutputFilenames()).to.deep.equal(['bundling-slots-inline-true-body.js']); var body = lassoPageResult.getSlotHtml('body'); expect(body).to.equal('\n'); } } ]; }; ================================================ FILE: test/autotests/bundling/slots-override/foo.css ================================================ foo_css ================================================ FILE: test/autotests/bundling/slots-override/foo.js ================================================ foo_js ================================================ FILE: test/autotests/bundling/slots-override/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/bundling/slots-override/test.js ================================================ var expect = require('chai').expect; var path = require('path'); exports.getLassoConfig = function() { return { fingerprintsEnabled: false, includeSlotNames: true }; }; exports.getInputs = function() { return [ { lassoOptions: { dependencies: [ { path: path.join(__dirname, 'foo.js'), slot: 'head' }, { path: path.join(__dirname, 'foo.css'), slot: 'body' } ] }, check(lassoPageResult, writerTracker) { expect(writerTracker.getOutputFilenames()).to.deep.equal([ 'bundling-slots-override-body.css', 'bundling-slots-override-head.js' ]); expect(writerTracker.getCodeForFilename('bundling-slots-override-body.css')).to.equal('foo_css'); expect(writerTracker.getCodeForFilename('bundling-slots-override-head.js')).to.equal('foo_js'); expect(lassoPageResult.getHeadHtml()).to.contain('bundling-slots-override-head.js'); expect(lassoPageResult.getBodyHtml()).to.contain('bundling-slots-override-body.css'); } } ]; }; ================================================ FILE: test/autotests/dep-require/async/bar.js ================================================ ================================================ FILE: test/autotests/dep-require/async/expected.json ================================================ { "dependencies": [ { "type": "commonjs-runtime" }, { "type": "commonjs-ready", "inline": "end" }, { "type": "commonjs-builtin", "name": "lasso-loader", "target": "/lasso-loader$x.x.x/src/index", "_sourceFile": "/node_modules/lasso-loader/src/index.js" }, { "type": "require", "path": "lasso-loader", "from": "/test/autotests/dep-require/async", "resolved": { "path": "/node_modules/lasso-loader/src/index.js", "meta": [ { "type": "builtin", "name": "lasso-loader", "target": "/node_modules/lasso-loader/src/index.js" } ], "clientPath": "/lasso-loader$x.x.x/src/index" } }, { "type": "commonjs-def", "path": "/autotest$0/foo", "file": "/test/autotests/dep-require/async/foo.js", "inspected": { "requires": [ { "path": "lasso-loader", "range": [ 0, 23 ], "argRange": [ 8, 22 ], "resolved": { "path": "/node_modules/lasso-loader/src/index.js", "meta": [ { "type": "builtin", "name": "lasso-loader", "target": "/node_modules/lasso-loader/src/index.js" } ], "clientPath": "/lasso-loader$x.x.x/src/index" } } ], "foundGlobals": {}, "asyncBlocks": [ { "requires": [ { "path": "./bar", "range": [ 57, 73 ], "argRange": [ 65, 72 ], "resolved": { "path": "/test/autotests/dep-require/async/bar.js", "meta": [], "clientPath": "/autotest$0/bar" } } ], "dependencies": [ { "type": "require", "path": "./bar" } ], "firstArgRange": [ 30, 96 ], "hasInlineDependencies": false, "hasFunctionBody": true, "name": "_HASH" } ], "lastModified": -1, "allRequires": [ { "path": "lasso-loader", "range": [ 0, 23 ], "argRange": [ 8, 22 ], "resolved": { "path": "/node_modules/lasso-loader/src/index.js", "meta": [ { "type": "builtin", "name": "lasso-loader", "target": "/node_modules/lasso-loader/src/index.js" } ], "clientPath": "/lasso-loader$x.x.x/src/index" } }, { "path": "./bar", "range": [ 57, 73 ], "argRange": [ 65, 72 ], "resolved": { "path": "/test/autotests/dep-require/async/bar.js", "meta": [], "clientPath": "/autotest$0/bar" } } ] }, "asyncBlocks": [ { "requires": [ { "path": "./bar", "range": [ 57, 73 ], "argRange": [ 65, 72 ], "resolved": { "path": "/test/autotests/dep-require/async/bar.js", "meta": [], "clientPath": "/autotest$0/bar" } } ], "dependencies": [ { "type": "require", "path": "./bar" } ], "firstArgRange": [ 30, 96 ], "hasInlineDependencies": false, "hasFunctionBody": true, "name": "_HASH" } ], "requireLastModified": -1 } ], "async": { "_HASH": [ { "type": "require", "path": "./bar" } ] }, "dirname": "/test/autotests/dep-require/async", "filename": "/test/autotests/dep-require/async/foo.js" } ================================================ FILE: test/autotests/dep-require/async/foo.js ================================================ require('lasso-loader').async(function() { var bar = require('./bar'); bar.sayHello(); }); ================================================ FILE: test/autotests/dep-require/async/package.json ================================================ { "name": "autotest" } ================================================ FILE: test/autotests/dep-require/async/test.js ================================================ exports.createDependency = function(dirname) { return { path: './foo', from: dirname }; }; ================================================ FILE: test/autotests/dep-require/builtin/expected.json ================================================ [ { "type": "commonjs-runtime" }, { "type": "commonjs-ready", "inline": "end" }, { "type": "commonjs-builtin", "name": "path", "target": "/path-browserify$1.0.1/index", "_sourceFile": "/node_modules/path-browserify/index.js" }, { "type": "require", "path": "path", "from": "/test/autotests/dep-require/builtin", "resolved": { "path": "/node_modules/path-browserify/index.js", "meta": [ { "type": "builtin", "name": "path", "target": "/node_modules/path-browserify/index.js" } ], "clientPath": "/path-browserify$1.0.1/index" } }, { "type": "commonjs-def", "path": "/autotest$0/foo", "file": "/test/autotests/dep-require/builtin/foo.js", "inspected": { "requires": [ { "path": "path", "range": [ 11, 26 ], "argRange": [ 19, 25 ], "resolved": { "path": "/node_modules/path-browserify/index.js", "meta": [ { "type": "builtin", "name": "path", "target": "/node_modules/path-browserify/index.js" } ], "clientPath": "/path-browserify$1.0.1/index" } } ], "foundGlobals": {}, "asyncBlocks": [], "lastModified": -1, "allRequires": [ { "path": "path", "range": [ 11, 26 ], "argRange": [ 19, 25 ], "resolved": { "path": "/node_modules/path-browserify/index.js", "meta": [ { "type": "builtin", "name": "path", "target": "/node_modules/path-browserify/index.js" } ], "clientPath": "/path-browserify$1.0.1/index" } } ] }, "requireLastModified": -1 } ] ================================================ FILE: test/autotests/dep-require/builtin/foo.js ================================================ var path = require('path'); path.join(__dirname, 'hello-world'); ================================================ FILE: test/autotests/dep-require/builtin/package.json ================================================ { "name": "autotest" } ================================================ FILE: test/autotests/dep-require/builtin/test.js ================================================ exports.createDependency = function(dirname) { return { path: './foo', from: dirname }; }; ================================================ FILE: test/autotests/dep-require/globals-custom/expected.json ================================================ [ { "type": "commonjs-runtime" }, { "type": "commonjs-installed", "parentPath": "/autotest$0", "childName": "foo", "childVersion": "1.2.0", "parentDir": "/test/autotests/dep-require/globals-custom" }, { "type": "commonjs-main", "dir": "/foo$1.2.0", "main": "", "_sourceFile": "/test/autotests/dep-require/globals-custom/node_modules/foo/index.js" }, { "type": "commonjs-ready", "inline": "end" }, { "type": "commonjs-def", "path": "/foo$1.2.0/index", "file": "/test/autotests/dep-require/globals-custom/node_modules/foo/index.js", "globals": [ "FOO" ], "inspected": { "requires": [], "foundGlobals": {}, "asyncBlocks": [], "lastModified": -1, "allRequires": [] }, "requireLastModified": -1 } ] ================================================ FILE: test/autotests/dep-require/globals-custom/package.json ================================================ { "name": "autotest" } ================================================ FILE: test/autotests/dep-require/globals-custom/test.js ================================================ exports.createDependency = function(dirname) { return { path: 'foo', from: dirname }; }; exports.getPluginConfig = function() { var globals = {}; var fooPath = require.resolve('foo'); globals[fooPath] = ['FOO']; return { globals: globals }; }; ================================================ FILE: test/autotests/dep-require/globals-jquery/expected.json ================================================ [ { "type": "commonjs-runtime" }, { "type": "commonjs-installed", "parentPath": "/autotest$0", "childName": "jquery", "childVersion": "1.2.0", "parentDir": "/test/autotests/dep-require/globals-jquery" }, { "type": "commonjs-main", "dir": "/jquery$1.2.0", "main": "", "_sourceFile": "/test/autotests/dep-require/globals-jquery/node_modules/jquery/index.js" }, { "type": "commonjs-ready", "inline": "end" }, { "type": "commonjs-def", "path": "/jquery$1.2.0/index", "file": "/test/autotests/dep-require/globals-jquery/node_modules/jquery/index.js", "globals": [ "$", "jQuery" ], "inspected": { "requires": [], "foundGlobals": {}, "asyncBlocks": [], "lastModified": -1, "allRequires": [] }, "requireLastModified": -1 } ] ================================================ FILE: test/autotests/dep-require/globals-jquery/package.json ================================================ { "name": "autotest" } ================================================ FILE: test/autotests/dep-require/globals-jquery/test.js ================================================ exports.createDependency = function(dirname) { return { path: 'jquery', from: dirname }; }; ================================================ FILE: test/autotests/dep-require/main-custom/bar/bar.js ================================================ ================================================ FILE: test/autotests/dep-require/main-custom/bar/package.json ================================================ { "main": "bar.js" } ================================================ FILE: test/autotests/dep-require/main-custom/expected.json ================================================ [ { "type": "commonjs-runtime" }, { "type": "commonjs-ready", "inline": "end" }, { "type": "commonjs-main", "dir": "/autotest$0/bar", "main": "bar", "_sourceFile": "/test/autotests/dep-require/main-custom/bar/bar.js" }, { "type": "require", "path": "./bar", "from": "/test/autotests/dep-require/main-custom", "resolved": { "path": "/test/autotests/dep-require/main-custom/bar/bar.js", "meta": [ { "type": "main", "dir": "/test/autotests/dep-require/main-custom/bar", "main": "/test/autotests/dep-require/main-custom/bar/bar.js" } ], "clientPath": "/autotest$0/bar/bar" } }, { "type": "commonjs-def", "path": "/autotest$0/foo", "file": "/test/autotests/dep-require/main-custom/foo.js", "inspected": { "requires": [ { "path": "./bar", "range": [ 0, 16 ], "argRange": [ 8, 15 ], "resolved": { "path": "/test/autotests/dep-require/main-custom/bar/bar.js", "meta": [ { "type": "main", "dir": "/test/autotests/dep-require/main-custom/bar", "main": "/test/autotests/dep-require/main-custom/bar/bar.js" } ], "clientPath": "/autotest$0/bar/bar" } } ], "foundGlobals": {}, "asyncBlocks": [], "lastModified": -1, "allRequires": [ { "path": "./bar", "range": [ 0, 16 ], "argRange": [ 8, 15 ], "resolved": { "path": "/test/autotests/dep-require/main-custom/bar/bar.js", "meta": [ { "type": "main", "dir": "/test/autotests/dep-require/main-custom/bar", "main": "/test/autotests/dep-require/main-custom/bar/bar.js" } ], "clientPath": "/autotest$0/bar/bar" } } ] }, "requireLastModified": -1 } ] ================================================ FILE: test/autotests/dep-require/main-custom/foo.js ================================================ require('./bar'); ================================================ FILE: test/autotests/dep-require/main-custom/package.json ================================================ { "name": "autotest" } ================================================ FILE: test/autotests/dep-require/main-custom/test.js ================================================ exports.createDependency = function(dirname) { return { path: './foo', from: dirname }; }; ================================================ FILE: test/autotests/dep-require/main-index/bar/index.js ================================================ ================================================ FILE: test/autotests/dep-require/main-index/expected.json ================================================ [ { "type": "commonjs-runtime" }, { "type": "commonjs-ready", "inline": "end" }, { "type": "commonjs-main", "dir": "/autotest$0/bar", "main": "", "_sourceFile": "/test/autotests/dep-require/main-index/bar/index.js" }, { "type": "require", "path": "./bar", "from": "/test/autotests/dep-require/main-index", "resolved": { "path": "/test/autotests/dep-require/main-index/bar/index.js", "meta": [ { "type": "main", "dir": "/test/autotests/dep-require/main-index/bar", "main": "/test/autotests/dep-require/main-index/bar/index.js" } ], "clientPath": "/autotest$0/bar/index" } }, { "type": "commonjs-def", "path": "/autotest$0/foo", "file": "/test/autotests/dep-require/main-index/foo.js", "inspected": { "requires": [ { "path": "./bar", "range": [ 0, 16 ], "argRange": [ 8, 15 ], "resolved": { "path": "/test/autotests/dep-require/main-index/bar/index.js", "meta": [ { "type": "main", "dir": "/test/autotests/dep-require/main-index/bar", "main": "/test/autotests/dep-require/main-index/bar/index.js" } ], "clientPath": "/autotest$0/bar/index" } } ], "foundGlobals": {}, "asyncBlocks": [], "lastModified": -1, "allRequires": [ { "path": "./bar", "range": [ 0, 16 ], "argRange": [ 8, 15 ], "resolved": { "path": "/test/autotests/dep-require/main-index/bar/index.js", "meta": [ { "type": "main", "dir": "/test/autotests/dep-require/main-index/bar", "main": "/test/autotests/dep-require/main-index/bar/index.js" } ], "clientPath": "/autotest$0/bar/index" } } ] }, "requireLastModified": -1 } ] ================================================ FILE: test/autotests/dep-require/main-index/foo.js ================================================ require('./bar'); ================================================ FILE: test/autotests/dep-require/main-index/module-no-dependencies/expected.json ================================================ [ { "type": "commonjs-runtime" }, { "type": "commonjs-ready", "inline": "end" }, { "type": "commonjs-def", "path": "/autotest$0/foo", "file": "/test/fixtures/dep-require/autotest/module-no-dependencies/foo.js", "inspected": { "requires": [], "foundGlobals": {}, "asyncBlocks": [], "lastModified": -1, "allRequires": [] }, "requireLastModified": -1 } ] ================================================ FILE: test/autotests/dep-require/main-index/module-no-dependencies/foo.js ================================================ console.log('foo'); ================================================ FILE: test/autotests/dep-require/main-index/module-no-dependencies/package.json ================================================ { "name": "autotest" } ================================================ FILE: test/autotests/dep-require/main-index/module-no-dependencies/test.js ================================================ exports.createDependency = function(dirname) { return { path: './foo', from: dirname }; }; ================================================ FILE: test/autotests/dep-require/main-index/package.json ================================================ { "name": "autotest" } ================================================ FILE: test/autotests/dep-require/main-index/test.js ================================================ exports.createDependency = function(dirname) { return { path: './foo', from: dirname }; }; ================================================ FILE: test/autotests/dep-require/main-require-redefined/bar/bar.js ================================================ ================================================ FILE: test/autotests/dep-require/main-require-redefined/bar/package.json ================================================ { "main": "bar.js" } ================================================ FILE: test/autotests/dep-require/main-require-redefined/expected.json ================================================ [ { "type": "commonjs-runtime" }, { "type": "commonjs-ready", "inline": "end" }, { "type": "commonjs-main", "dir": "/autotest$0/bar", "main": "bar", "_sourceFile": "/test/autotests/dep-require/main-require-redefined/bar/bar.js" }, { "type": "require", "path": "./bar", "from": "/test/autotests/dep-require/main-require-redefined", "resolved": { "path": "/test/autotests/dep-require/main-require-redefined/bar/bar.js", "meta": [ { "type": "main", "dir": "/test/autotests/dep-require/main-require-redefined/bar", "main": "/test/autotests/dep-require/main-require-redefined/bar/bar.js" } ], "clientPath": "/autotest$0/bar/bar" } }, { "type": "commonjs-def", "path": "/autotest$0/foo", "file": "/test/autotests/dep-require/main-require-redefined/foo.js", "inspected": { "requires": [ { "path": "./bar", "range": [ 0, 16 ], "argRange": [ 8, 15 ], "resolved": { "path": "/test/autotests/dep-require/main-require-redefined/bar/bar.js", "meta": [ { "type": "main", "dir": "/test/autotests/dep-require/main-require-redefined/bar", "main": "/test/autotests/dep-require/main-require-redefined/bar/bar.js" } ], "clientPath": "/autotest$0/bar/bar" } } ], "foundGlobals": {}, "asyncBlocks": [], "lastModified": -1, "allRequires": [ { "path": "./bar", "range": [ 0, 16 ], "argRange": [ 8, 15 ], "resolved": { "path": "/test/autotests/dep-require/main-require-redefined/bar/bar.js", "meta": [ { "type": "main", "dir": "/test/autotests/dep-require/main-require-redefined/bar", "main": "/test/autotests/dep-require/main-require-redefined/bar/bar.js" } ], "clientPath": "/autotest$0/bar/bar" } } ] }, "requireLastModified": -1 } ] ================================================ FILE: test/autotests/dep-require/main-require-redefined/foo.js ================================================ require('./bar'); (function() { var require = function() {}; require('some-thing'); }()); ================================================ FILE: test/autotests/dep-require/main-require-redefined/package.json ================================================ { "name": "autotest" } ================================================ FILE: test/autotests/dep-require/main-require-redefined/test.js ================================================ exports.createDependency = function(dirname) { return { path: './foo', from: dirname }; }; ================================================ FILE: test/autotests/dep-require/remap-browser-override/expected.json ================================================ [ { "type": "commonjs-runtime" }, { "type": "commonjs-remap", "from": "/autotest$0/foo", "to": "/autotest$0/foo-browser", "fromFile": "/test/autotests/dep-require/remap-browser-override/foo.js" }, { "type": "commonjs-ready", "inline": "end" }, { "type": "commonjs-def", "path": "/autotest$0/foo-browser", "file": "/test/autotests/dep-require/remap-browser-override/foo-browser.js", "inspected": { "requires": [], "foundGlobals": {}, "asyncBlocks": [], "lastModified": -1, "allRequires": [] }, "requireLastModified": -1 } ] ================================================ FILE: test/autotests/dep-require/remap-browser-override/foo-browser.js ================================================ console.log('foo-browser'); ================================================ FILE: test/autotests/dep-require/remap-browser-override/foo.js ================================================ console.log('foo'); ================================================ FILE: test/autotests/dep-require/remap-browser-override/package.json ================================================ { "name": "autotest", "browser": { "./foo.js": "./foo-browser.js" } } ================================================ FILE: test/autotests/dep-require/remap-browser-override/test.js ================================================ exports.createDependency = function(dirname) { return { path: './foo', from: dirname }; }; ================================================ FILE: test/autotests/dep-require/require-installed/expected.json ================================================ [ { "type": "commonjs-runtime" }, { "type": "commonjs-installed", "parentPath": "/autotest$0", "childName": "installed-bar", "childVersion": "1.2.0", "parentDir": "/test/autotests/dep-require/require-installed" }, { "type": "commonjs-main", "dir": "/installed-bar$1.2.0", "main": "", "_sourceFile": "/test/autotests/dep-require/require-installed/node_modules/installed-bar/index.js" }, { "type": "commonjs-ready", "inline": "end" }, { "type": "commonjs-def", "path": "/installed-bar$1.2.0/index", "file": "/test/autotests/dep-require/require-installed/node_modules/installed-bar/index.js", "inspected": { "requires": [], "foundGlobals": {}, "asyncBlocks": [], "lastModified": -1, "allRequires": [] }, "requireLastModified": -1 } ] ================================================ FILE: test/autotests/dep-require/require-installed/foo.js ================================================ require('installed-bar'); ================================================ FILE: test/autotests/dep-require/require-installed/package.json ================================================ { "name": "autotest" } ================================================ FILE: test/autotests/dep-require/require-installed/test.js ================================================ var path = require('path'); exports.createDependency = function(dirname) { return { path: 'installed-bar', from: dirname }; }; ================================================ FILE: test/autotests/dep-require/require-installed-scoped/expected.json ================================================ [ { "type": "commonjs-runtime" }, { "type": "commonjs-installed", "parentPath": "/autotest$0", "childName": "@foo/bar", "childVersion": "1.0.0", "parentDir": "/test/autotests/dep-require/require-installed-scoped" }, { "type": "commonjs-main", "dir": "/@foo/bar$1.0.0", "main": "", "_sourceFile": "/test/autotests/dep-require/require-installed-scoped/node_modules/@foo/bar/index.js" }, { "type": "commonjs-ready", "inline": "end" }, { "type": "commonjs-def", "path": "/@foo/bar$1.0.0/index", "file": "/test/autotests/dep-require/require-installed-scoped/node_modules/@foo/bar/index.js", "inspected": { "requires": [], "foundGlobals": {}, "asyncBlocks": [], "lastModified": -1, "allRequires": [] }, "requireLastModified": -1 } ] ================================================ FILE: test/autotests/dep-require/require-installed-scoped/package.json ================================================ { "name": "autotest" } ================================================ FILE: test/autotests/dep-require/require-installed-scoped/test.js ================================================ exports.createDependency = function(dirname) { return { path: '@foo/bar', from: dirname }; }; ================================================ FILE: test/autotests/dep-require/require-search-path/app-modules/bar/index.js ================================================ ================================================ FILE: test/autotests/dep-require/require-search-path/app-modules/bar/package.json ================================================ { "name": "installed-bar", "version": "1.2.0" } ================================================ FILE: test/autotests/dep-require/require-search-path/expected.json ================================================ [ { "type": "commonjs-runtime" }, { "type": "commonjs-search-path", "path": "/require-search-path$0/app-modules/" }, { "type": "commonjs-main", "dir": "/installed-bar$1.2.0", "main": "", "_sourceFile": "/test/autotests/dep-require/require-search-path/app-modules/bar/index.js" }, { "type": "commonjs-ready", "inline": "end" }, { "type": "commonjs-def", "path": "/installed-bar$1.2.0/index", "file": "/test/autotests/dep-require/require-search-path/app-modules/bar/index.js", "inspected": { "requires": [], "foundGlobals": {}, "asyncBlocks": [], "lastModified": -1, "allRequires": [] }, "requireLastModified": -1 } ] ================================================ FILE: test/autotests/dep-require/require-search-path/package.json ================================================ { "name": "require-search-path" } ================================================ FILE: test/autotests/dep-require/require-search-path/test.js ================================================ var path = require('path'); exports.createDependency = function(dirname) { return { path: 'bar', from: dirname }; }; exports.searchPath = path.join(__dirname, 'app-modules'); ================================================ FILE: test/autotests/dep-require/transitive/bar.js ================================================ ================================================ FILE: test/autotests/dep-require/transitive/expected.json ================================================ [ { "type": "commonjs-runtime" }, { "type": "commonjs-ready", "inline": "end" }, { "type": "require", "path": "./bar", "from": "/test/autotests/dep-require/transitive", "resolved": { "path": "/test/autotests/dep-require/transitive/bar.js", "meta": [], "clientPath": "/autotest$0/bar" } }, { "type": "commonjs-def", "path": "/autotest$0/foo", "file": "/test/autotests/dep-require/transitive/foo.js", "inspected": { "requires": [ { "path": "./bar", "range": [ 0, 16 ], "argRange": [ 8, 15 ], "resolved": { "path": "/test/autotests/dep-require/transitive/bar.js", "meta": [], "clientPath": "/autotest$0/bar" } } ], "foundGlobals": {}, "asyncBlocks": [], "lastModified": -1, "allRequires": [ { "path": "./bar", "range": [ 0, 16 ], "argRange": [ 8, 15 ], "resolved": { "path": "/test/autotests/dep-require/transitive/bar.js", "meta": [], "clientPath": "/autotest$0/bar" } } ] }, "requireLastModified": -1 } ] ================================================ FILE: test/autotests/dep-require/transitive/foo.js ================================================ require('./bar'); ================================================ FILE: test/autotests/dep-require/transitive/package.json ================================================ { "name": "autotest" } ================================================ FILE: test/autotests/dep-require/transitive/test.js ================================================ exports.createDependency = function(dirname) { return { path: './foo', from: dirname }; }; ================================================ FILE: test/autotests/dep-require/transitive-resolved/bar.js ================================================ ================================================ FILE: test/autotests/dep-require/transitive-resolved/expected.json ================================================ [ { "type": "commonjs-runtime" }, { "type": "commonjs-ready", "inline": "end" }, { "type": "commonjs-def", "path": "/autotest$0/bar", "file": "/test/autotests/dep-require/transitive-resolved/bar.js", "inspected": { "requires": [], "foundGlobals": {}, "asyncBlocks": [], "lastModified": -1, "allRequires": [] }, "requireLastModified": -1 } ] ================================================ FILE: test/autotests/dep-require/transitive-resolved/foo.js ================================================ require('./bar'); ================================================ FILE: test/autotests/dep-require/transitive-resolved/package.json ================================================ { "name": "autotest" } ================================================ FILE: test/autotests/dep-require/transitive-resolved/test.js ================================================ exports.createDependency = function(dirname) { return { path: require.resolve('./bar.js'), from: __dirname }; }; ================================================ FILE: test/autotests/dep-require/virtual-module/expected.json ================================================ [ { "type": "commonjs-runtime" }, { "type": "commonjs-ready", "inline": "end" }, { "type": "commonjs-def", "path": "/virtual-module/something.foo", "file": "/test/autotests/dep-require/virtual-module/something.foo", "inspected": { "requires": [], "foundGlobals": {}, "asyncBlocks": [], "lastModified": -1, "allRequires": [] }, "requireLastModified": -1 } ] ================================================ FILE: test/autotests/dep-require/virtual-module/package.json ================================================ { "name": "autotest" } ================================================ FILE: test/autotests/dep-require/virtual-module/test.js ================================================ exports.createDependency = function(dirname) { return { virtualModule: { path: __dirname + '/something.foo', clientPath: '/virtual-module/something.foo', read(lassoContext, callback) { setTimeout(function() { callback(null, 'exports.hello = "world"; exports.filename = __filename;'); }, 10); } } }; }; ================================================ FILE: test/autotests/dep-transport-define/async/bar.js ================================================ ================================================ FILE: test/autotests/dep-transport-define/async/expected.js ================================================ $_mod.def("/autotest$0/foo", function(require, exports, module, __filename, __dirname) { require('/lasso-loader$x.x.x/src/index'/*'lasso-loader'*/).async("_HASH", function() { var bar = require('/autotest$0/bar'/*'./bar'*/); bar.sayHello(); }); }); ================================================ FILE: test/autotests/dep-transport-define/async/foo.js ================================================ require('lasso-loader').async(function() { var bar = require('./bar'); bar.sayHello(); }); ================================================ FILE: test/autotests/dep-transport-define/async/package.json ================================================ { "name": "autotest" } ================================================ FILE: test/autotests/dep-transport-define/async/test.js ================================================ exports.createDependency = function(dirname) { return { path: './foo', from: dirname }; }; ================================================ FILE: test/autotests/dep-transport-define/builtin/expected.js ================================================ $_mod.def("/autotest$0/foo", function(require, exports, module, __filename, __dirname) { var path = require('/path-browserify$1.0.1/index'/*'path'*/); }); ================================================ FILE: test/autotests/dep-transport-define/builtin/foo.js ================================================ var path = require('path'); ================================================ FILE: test/autotests/dep-transport-define/builtin/package.json ================================================ { "name": "autotest" } ================================================ FILE: test/autotests/dep-transport-define/builtin/test.js ================================================ exports.createDependency = function(dirname) { return { path: './foo', from: dirname }; }; ================================================ FILE: test/autotests/dep-transport-define/globals-foo/expected.js ================================================ $_mod.def("/foo$1.0.0/index", function(require, exports, module, __filename, __dirname) { module.exports = function Foo() {}; },{"globals":["FOO"]}); ================================================ FILE: test/autotests/dep-transport-define/globals-foo/package.json ================================================ { "name": "autotest" } ================================================ FILE: test/autotests/dep-transport-define/globals-foo/test.js ================================================ exports.createDependency = function(dirname) { return { path: 'foo', from: dirname }; }; exports.getPluginConfig = function() { var globals = {}; var fooPath = require.resolve('foo'); globals[fooPath] = ['FOO']; return { globals: globals }; }; ================================================ FILE: test/autotests/dep-transport-define/globals-jquery/expected.js ================================================ $_mod.def("/jquery$1.2.0/index", function(require, exports, module, __filename, __dirname) { module.exports = function jQuery() {}; },{"globals":["$","jQuery"]}); ================================================ FILE: test/autotests/dep-transport-define/globals-jquery/package.json ================================================ { "name": "autotest" } ================================================ FILE: test/autotests/dep-transport-define/globals-jquery/test.js ================================================ exports.createDependency = function(dirname) { return { path: 'jquery', from: dirname }; }; exports.getPluginConfig = function() { var globals = {}; return { globals: globals }; }; ================================================ FILE: test/autotests/dep-transport-define/main-custom/bar/bar.js ================================================ ================================================ FILE: test/autotests/dep-transport-define/main-custom/bar/package.json ================================================ { "main": "bar.js" } ================================================ FILE: test/autotests/dep-transport-define/main-custom/expected.js ================================================ $_mod.def("/autotest$0/foo", function(require, exports, module, __filename, __dirname) { require('/autotest$0/bar/bar'/*'./bar'*/); }); ================================================ FILE: test/autotests/dep-transport-define/main-custom/foo.js ================================================ require('./bar'); ================================================ FILE: test/autotests/dep-transport-define/main-custom/package.json ================================================ { "name": "autotest" } ================================================ FILE: test/autotests/dep-transport-define/main-custom/test.js ================================================ exports.createDependency = function(dirname) { return { path: './foo', from: dirname }; }; ================================================ FILE: test/autotests/dep-transport-define/main-index/bar/index.js ================================================ ================================================ FILE: test/autotests/dep-transport-define/main-index/expected.js ================================================ $_mod.def("/autotest$0/foo", function(require, exports, module, __filename, __dirname) { require('/autotest$0/bar/index'/*'./bar'*/); }); ================================================ FILE: test/autotests/dep-transport-define/main-index/foo.js ================================================ require('./bar'); ================================================ FILE: test/autotests/dep-transport-define/main-index/package.json ================================================ { "name": "autotest" } ================================================ FILE: test/autotests/dep-transport-define/main-index/test.js ================================================ exports.createDependency = function(dirname) { return { path: './foo', from: dirname }; }; ================================================ FILE: test/autotests/dep-transport-define/module-no-dependencies/expected.js ================================================ $_mod.def("/autotest$0/foo", function(require, exports, module, __filename, __dirname) { console.log('foo'); }); ================================================ FILE: test/autotests/dep-transport-define/module-no-dependencies/foo.js ================================================ console.log('foo'); ================================================ FILE: test/autotests/dep-transport-define/module-no-dependencies/package.json ================================================ { "name": "autotest" } ================================================ FILE: test/autotests/dep-transport-define/module-no-dependencies/test.js ================================================ exports.createDependency = function(dirname) { return { path: './foo', from: dirname }; }; ================================================ FILE: test/autotests/dep-transport-define/remap-void/bar.js ================================================ console.log('server only'); ================================================ FILE: test/autotests/dep-transport-define/remap-void/expected.js ================================================ $_mod.def("/autotest$0/foo", function(require, exports, module, __filename, __dirname) { var bar = ({})/*require('./bar')*/; bar = ({})/*require('./bar')*/; }); ================================================ FILE: test/autotests/dep-transport-define/remap-void/foo.js ================================================ var bar = require('./bar'); bar = require('./bar'); ================================================ FILE: test/autotests/dep-transport-define/remap-void/package.json ================================================ { "name": "autotest", "browser": { "./bar.js": false } } ================================================ FILE: test/autotests/dep-transport-define/remap-void/test.js ================================================ exports.createDependency = function(dirname) { return { path: './foo', from: dirname }; }; ================================================ FILE: test/autotests/dep-transport-define/require-installed/expected.js ================================================ $_mod.def("/installed-bar$1.2.0/index", function(require, exports, module, __filename, __dirname) { console.log('installed-bar'); }); ================================================ FILE: test/autotests/dep-transport-define/require-installed/foo.js ================================================ require('installed-bar'); ================================================ FILE: test/autotests/dep-transport-define/require-installed/package.json ================================================ { "name": "autotest" } ================================================ FILE: test/autotests/dep-transport-define/require-installed/test.js ================================================ var path = require('path'); exports.createDependency = function(dirname) { return { path: 'installed-bar', from: dirname }; }; ================================================ FILE: test/autotests/dep-transport-define/require-installed-scoped/expected.js ================================================ $_mod.def("/@foo/bar$1.0.0/index", function(require, exports, module, __filename, __dirname) { console.log('@foo/bar'); }); ================================================ FILE: test/autotests/dep-transport-define/require-installed-scoped/package.json ================================================ { "name": "autotest" } ================================================ FILE: test/autotests/dep-transport-define/require-installed-scoped/test.js ================================================ exports.createDependency = function(dirname) { return { path: '@foo/bar', from: dirname }; }; ================================================ FILE: test/autotests/dep-transport-define/require-search-path/app-modules/bar/index.js ================================================ console.log('app-modules/bar'); ================================================ FILE: test/autotests/dep-transport-define/require-search-path/app-modules/bar/package.json ================================================ { "name": "bar", "version": "1.2.0" } ================================================ FILE: test/autotests/dep-transport-define/require-search-path/expected.js ================================================ $_mod.def("/autotest$0/foo", function(require, exports, module, __filename, __dirname) { var bar = require('/bar$1.2.0/index'/*'bar'*/); }); ================================================ FILE: test/autotests/dep-transport-define/require-search-path/foo.js ================================================ var bar = require('bar'); ================================================ FILE: test/autotests/dep-transport-define/require-search-path/package.json ================================================ { "name": "autotest" } ================================================ FILE: test/autotests/dep-transport-define/require-search-path/test.js ================================================ var path = require('path'); exports.createDependency = function(dirname) { return { from: dirname, path: './foo' }; }; exports.searchPath = path.join(__dirname, 'app-modules'); ================================================ FILE: test/autotests/dep-transport-define/transform-browserify/bar.js ================================================ console.log('hello'); ================================================ FILE: test/autotests/dep-transport-define/transform-browserify/expected.js ================================================ $_mod.def("/autotest$0/bar", function(require, exports, module, __filename, __dirname) { console.log('hello');|my-transform|file=/bar.js }); ================================================ FILE: test/autotests/dep-transport-define/transform-browserify/my-transform.js ================================================ 'use strict'; var stream = require('stream'); class MyTransform extends stream.Transform { constructor(file) { super(); this.file = file; this.data = ''; } _transform(buf, enc, callback) { // Collect all of the data as it is streamed in and just concatenate to a our data string // but don't actually stream out any data yet this.data += buf; callback(); } _flush(callback) { this.push(this.data + '|my-transform|file=' + this.file); callback(); } } module.exports = function(file) { return new MyTransform(file); }; ================================================ FILE: test/autotests/dep-transport-define/transform-browserify/package.json ================================================ { "name": "autotest" } ================================================ FILE: test/autotests/dep-transport-define/transform-browserify/test.js ================================================ exports.getPluginConfig = function() { return { transforms: [ require('./my-transform') ] }; }; exports.createDependency = function(dirname) { return { from: dirname, path: './bar' }; }; ================================================ FILE: test/autotests/dep-transport-define/transform-browserify-config/bar.js ================================================ console.log('hello'); ================================================ FILE: test/autotests/dep-transport-define/transform-browserify-config/expected.js ================================================ $_mod.def("/autotest$0/bar", function(require, exports, module, __filename, __dirname) { console.log('WORLD');|filename=/bar.js }); ================================================ FILE: test/autotests/dep-transport-define/transform-browserify-config/my-transform.js ================================================ 'use strict'; var stream = require('stream'); class MyTransform extends stream.Transform { constructor(file, opts) { super(); this.file = file; this.data = ''; this.replacement = opts.replacement; } _transform(buf, enc, callback) { // Collect all of the data as it is streamed in and just concatenate to a our data string // but don't actually stream out any data yet this.data += buf; callback(); } _flush(callback) { var replacement = this.replacement; this.push(this.data.replace(/hello/g, replacement) + '|filename=' + this.file); callback(); } } module.exports = function(file, opts) { return new MyTransform(file, opts); }; ================================================ FILE: test/autotests/dep-transport-define/transform-browserify-config/package.json ================================================ { "name": "autotest" } ================================================ FILE: test/autotests/dep-transport-define/transform-browserify-config/test.js ================================================ exports.getPluginConfig = function() { return { transforms: [ { transform: require('./my-transform'), config: { replacement: 'WORLD' } } ] }; }; exports.createDependency = function(dirname) { return { from: dirname, path: './bar' }; }; ================================================ FILE: test/autotests/dep-transport-define/transform-multiple/bar.js ================================================ console.log('hello world foo bar baz'); ================================================ FILE: test/autotests/dep-transport-define/transform-multiple/expected.js ================================================ $_mod.def("/autotest$0/bar", function(require, exports, module, __filename, __dirname) { console.log('HELLO WORLD FOO bar baz'); }); ================================================ FILE: test/autotests/dep-transport-define/transform-multiple/package.json ================================================ { "name": "autotest" } ================================================ FILE: test/autotests/dep-transport-define/transform-multiple/test.js ================================================ exports.getPluginConfig = function() { return { transforms: [ require('./transform-a'), { transform: require('./transform-b') }, { transform: require('./transform-c'), config: { replacement: 'FOO' } } ] }; }; exports.createDependency = function(dirname) { return { from: dirname, path: './bar' }; }; ================================================ FILE: test/autotests/dep-transport-define/transform-multiple/transform-a.js ================================================ 'use strict'; var expect = require('chai').expect; var stream = require('stream'); class MyTransform extends stream.Transform { constructor(file) { super(); this.file = file; this.data = ''; } _transform(buf, enc, callback) { // Collect all of the data as it is streamed in and just concatenate to a our data string // but don't actually stream out any data yet this.data += buf; callback(); } _flush(callback) { this.push(this.data.replace(/hello/g, 'HELLO')); callback(); } } module.exports = { id: __filename, stream: true, createTransform() { return function myTransform(file, lassoContext) { expect(lassoContext.isMockLassoContext).to.equal(true); return new MyTransform(file); }; } }; ================================================ FILE: test/autotests/dep-transport-define/transform-multiple/transform-b.js ================================================ 'use strict'; var stream = require('stream'); class MyTransform extends stream.Transform { constructor(file) { super(); this.file = file; this.data = ''; } _transform(buf, enc, callback) { // Collect all of the data as it is streamed in and just concatenate to a our data string // but don't actually stream out any data yet this.data += buf; callback(); } _flush(callback) { this.push(this.data.replace(/world/g, 'WORLD')); callback(); } } module.exports = function(file) { return new MyTransform(file); }; ================================================ FILE: test/autotests/dep-transport-define/transform-multiple/transform-c.js ================================================ module.exports = { id: __filename, stream: false, createTransform(transformConfig) { return function myTransform(code, lassoContext) { return code.replace(/foo/g, transformConfig.replacement); }; } }; ================================================ FILE: test/autotests/dep-transport-define/transform-promise/bar.js ================================================ console.log('hello'); ================================================ FILE: test/autotests/dep-transport-define/transform-promise/expected.js ================================================ $_mod.def("/autotest$0/bar", function(require, exports, module, __filename, __dirname) { console.log('HELLO'); }); ================================================ FILE: test/autotests/dep-transport-define/transform-promise/my-transform.js ================================================ module.exports = { id: __filename, stream: false, createTransform(transformConfig) { return function myTransform(code, lassoContext) { return new Promise(function(resolve, reject) { setTimeout(function() { resolve(code.replace(/hello/g, 'HELLO')); }, 10); }); }; } }; ================================================ FILE: test/autotests/dep-transport-define/transform-promise/package.json ================================================ { "name": "autotest" } ================================================ FILE: test/autotests/dep-transport-define/transform-promise/test.js ================================================ exports.getPluginConfig = function() { return { transforms: [ require('./my-transform') ] }; }; exports.createDependency = function(dirname) { return { from: dirname, path: './bar' }; }; ================================================ FILE: test/autotests/dep-transport-define/transform-stream/bar.js ================================================ console.log('hello'); ================================================ FILE: test/autotests/dep-transport-define/transform-stream/expected.js ================================================ $_mod.def("/autotest$0/bar", function(require, exports, module, __filename, __dirname) { console.log('WORLD');|filename=/bar.js }); ================================================ FILE: test/autotests/dep-transport-define/transform-stream/my-transform.js ================================================ 'use strict'; var expect = require('chai').expect; var stream = require('stream'); class MyTransform extends stream.Transform { constructor(file) { super(); this.file = file; this.data = ''; } _transform(buf, enc, callback) { // Collect all of the data as it is streamed in and just concatenate to a our data string // but don't actually stream out any data yet this.data += buf; callback(); } _flush(callback) { this.push(this.data.replace(/hello/g, 'WORLD') + '|filename=' + this.file); callback(); } } module.exports = { id: __filename, stream: true, createTransform() { return function myTransform(file, lassoContext) { expect(lassoContext.isMockLassoContext).to.equal(true); return new MyTransform(file); }; } }; ================================================ FILE: test/autotests/dep-transport-define/transform-stream/package.json ================================================ { "name": "autotest" } ================================================ FILE: test/autotests/dep-transport-define/transform-stream/test.js ================================================ exports.getPluginConfig = function() { return { transforms: [ require('./my-transform') ] }; }; exports.createDependency = function(dirname) { return { from: dirname, path: './bar' }; }; ================================================ FILE: test/autotests/dep-transport-define/transform-stream-config/bar.js ================================================ console.log('hello'); ================================================ FILE: test/autotests/dep-transport-define/transform-stream-config/expected.js ================================================ $_mod.def("/autotest$0/bar", function(require, exports, module, __filename, __dirname) { console.log('WORLD');|filename=/bar.js }); ================================================ FILE: test/autotests/dep-transport-define/transform-stream-config/my-transform.js ================================================ 'use strict'; var expect = require('chai').expect; var stream = require('stream'); class MyTransform extends stream.Transform { constructor(file, opts) { super(); this.file = file; this.data = ''; this.replacement = opts.replacement; } _transform(buf, enc, callback) { // Collect all of the data as it is streamed in and just concatenate to a our data string // but don't actually stream out any data yet this.data += buf; callback(); } _flush(callback) { var replacement = this.replacement; this.push(this.data.replace(/hello/g, replacement) + '|filename=' + this.file); callback(); } } module.exports = { id: __filename, stream: true, createTransform(transformConfig) { return function myTransform(file, lassoContext) { expect(lassoContext.isMockLassoContext).to.equal(true); return new MyTransform(file, transformConfig); }; } }; ================================================ FILE: test/autotests/dep-transport-define/transform-stream-config/package.json ================================================ { "name": "autotest" } ================================================ FILE: test/autotests/dep-transport-define/transform-stream-config/test.js ================================================ exports.getPluginConfig = function() { return { transforms: [ { transform: require('./my-transform'), config: { replacement: 'WORLD' } } ] }; }; exports.createDependency = function(dirname) { return { from: dirname, path: './bar' }; }; ================================================ FILE: test/autotests/dep-transport-define/transform-stream-no-config/bar.js ================================================ console.log('hello'); ================================================ FILE: test/autotests/dep-transport-define/transform-stream-no-config/expected.js ================================================ $_mod.def("/autotest$0/bar", function(require, exports, module, __filename, __dirname) { console.log('WORLD');|filename=/bar.js }); ================================================ FILE: test/autotests/dep-transport-define/transform-stream-no-config/my-transform.js ================================================ 'use strict'; var expect = require('chai').expect; var stream = require('stream'); class MyTransform extends stream.Transform { constructor(file) { super(); this.file = file; this.data = ''; } _transform(buf, enc, callback) { // Collect all of the data as it is streamed in and just concatenate to a our data string // but don't actually stream out any data yet this.data += buf; callback(); } _flush(callback) { this.push(this.data.replace(/hello/g, 'WORLD') + '|filename=' + this.file); callback(); } } module.exports = { id: __filename, stream: true, createTransform() { return function myTransform(file, lassoContext) { expect(lassoContext.isMockLassoContext).to.equal(true); return new MyTransform(file); }; } }; ================================================ FILE: test/autotests/dep-transport-define/transform-stream-no-config/package.json ================================================ { "name": "autotest" } ================================================ FILE: test/autotests/dep-transport-define/transform-stream-no-config/test.js ================================================ exports.getPluginConfig = function() { return { transforms: [ { transform: require('./my-transform') } ] }; }; exports.createDependency = function(dirname) { return { from: dirname, path: './bar' }; }; ================================================ FILE: test/autotests/dep-transport-define/transform-synchronous/bar.js ================================================ console.log('hello'); ================================================ FILE: test/autotests/dep-transport-define/transform-synchronous/expected.js ================================================ $_mod.def("/autotest$0/bar", function(require, exports, module, __filename, __dirname) { console.log('HELLO'); }); ================================================ FILE: test/autotests/dep-transport-define/transform-synchronous/my-transform.js ================================================ module.exports = { id: __filename, stream: false, createTransform(transformConfig) { return function myTransform(code, lassoContext) { return code.replace(/hello/g, 'HELLO'); }; } }; ================================================ FILE: test/autotests/dep-transport-define/transform-synchronous/package.json ================================================ { "name": "autotest" } ================================================ FILE: test/autotests/dep-transport-define/transform-synchronous/test.js ================================================ exports.getPluginConfig = function() { return { transforms: [ require('./my-transform') ] }; }; exports.createDependency = function(dirname) { return { from: dirname, path: './bar' }; }; ================================================ FILE: test/autotests/dep-transport-define/transform-synchronous-config/bar.js ================================================ console.log('hello'); ================================================ FILE: test/autotests/dep-transport-define/transform-synchronous-config/expected.js ================================================ $_mod.def("/autotest$0/bar", function(require, exports, module, __filename, __dirname) { console.log('WORLD'); }); ================================================ FILE: test/autotests/dep-transport-define/transform-synchronous-config/my-transform.js ================================================ module.exports = { id: __filename, stream: false, createTransform(transformConfig) { return function myTransform(code, lassoContext) { return code.replace(/hello/g, transformConfig.replacement); }; } }; ================================================ FILE: test/autotests/dep-transport-define/transform-synchronous-config/package.json ================================================ { "name": "autotest" } ================================================ FILE: test/autotests/dep-transport-define/transform-synchronous-config/test.js ================================================ exports.getPluginConfig = function() { return { transforms: [ { transform: require('./my-transform'), config: { replacement: 'WORLD' } } ] }; }; exports.createDependency = function(dirname) { return { from: dirname, path: './bar' }; }; ================================================ FILE: test/autotests/dep-transport-define/transitive/bar.js ================================================ $_mod.def("/foo", function(require, exports, module, __filename, __dirname) { require('/bar'/*'./bar'*/); }); ================================================ FILE: test/autotests/dep-transport-define/transitive/expected.js ================================================ $_mod.def("/autotest$0/foo", function(require, exports, module, __filename, __dirname) { require('/autotest$0/bar'/*'./bar'*/); }); ================================================ FILE: test/autotests/dep-transport-define/transitive/foo.js ================================================ require('./bar'); ================================================ FILE: test/autotests/dep-transport-define/transitive/package.json ================================================ { "name": "autotest" } ================================================ FILE: test/autotests/dep-transport-define/transitive/test.js ================================================ exports.createDependency = function(dirname) { return { path: './foo', from: dirname }; }; ================================================ FILE: test/autotests/dep-transport-define/transitive-resolved/bar.js ================================================ console.log('bar'); ================================================ FILE: test/autotests/dep-transport-define/transitive-resolved/expected.js ================================================ $_mod.def("/autotest$0/bar", function(require, exports, module, __filename, __dirname) { console.log('bar'); }); ================================================ FILE: test/autotests/dep-transport-define/transitive-resolved/package.json ================================================ { "name": "autotest" } ================================================ FILE: test/autotests/dep-transport-define/transitive-resolved/test.js ================================================ exports.createDependency = function(dirname) { return { from: dirname, path: './bar' }; }; ================================================ FILE: test/autotests/dep-transport-installed/simple/expected.js ================================================ $_mod.installed("test/fixtures/dep-require/autotest/require-installed", "installed-bar", "1.2.0"); ================================================ FILE: test/autotests/dep-transport-installed/simple/package.json ================================================ { "name": "autotest" } ================================================ FILE: test/autotests/dep-transport-installed/simple/test.js ================================================ exports.createDependency = function(dirname) { return { "parentPath": "/test/fixtures/dep-require/autotest/require-installed", "childName": "installed-bar", "childVersion": "1.2.0" }; }; ================================================ FILE: test/autotests/dep-transport-main/simple/expected.js ================================================ $_mod.main("/test/fixtures/dep-require/autotest/main-index/bar", ""); ================================================ FILE: test/autotests/dep-transport-main/simple/package.json ================================================ { "name": "autotest" } ================================================ FILE: test/autotests/dep-transport-main/simple/test.js ================================================ exports.createDependency = function(dirname) { return { "type": "commonjs-main", "dir": "/test/fixtures/dep-require/autotest/main-index/bar", "main": "" }; }; ================================================ FILE: test/autotests/dep-transport-ready/simple/expected.js ================================================ $_mod.ready(); ================================================ FILE: test/autotests/dep-transport-ready/simple/package.json ================================================ { "name": "autotest" } ================================================ FILE: test/autotests/dep-transport-ready/simple/test.js ================================================ exports.createDependency = function(dirname) { return {}; }; ================================================ FILE: test/autotests/dep-transport-remap/simple/expected.js ================================================ $_mod.remap("/foo", "/foo-browser"); ================================================ FILE: test/autotests/dep-transport-remap/simple/package.json ================================================ { "name": "autotest" } ================================================ FILE: test/autotests/dep-transport-remap/simple/test.js ================================================ exports.createDependency = function(dirname) { return { from: '/foo', to: '/foo-browser' }; }; ================================================ FILE: test/autotests/dep-transport-run/no-wait/expected.js ================================================ $_mod.run("/foo",{"wait":false}); ================================================ FILE: test/autotests/dep-transport-run/no-wait/package.json ================================================ { "name": "autotest" } ================================================ FILE: test/autotests/dep-transport-run/no-wait/test.js ================================================ exports.createDependency = function(dirname) { return { path: '/foo', wait: false }; }; ================================================ FILE: test/autotests/dep-transport-run/wait/expected.js ================================================ $_mod.run("/foo"); ================================================ FILE: test/autotests/dep-transport-run/wait/package.json ================================================ { "name": "autotest" } ================================================ FILE: test/autotests/dep-transport-run/wait/test.js ================================================ exports.createDependency = function(dirname) { return { path: '/foo', wait: true }; }; ================================================ FILE: test/autotests/dependency-walker/flat/browser.json ================================================ { "dependencies": [ "./foo.js" ] } ================================================ FILE: test/autotests/dependency-walker/flat/expected.txt ================================================ [package: path="/browser.json"] + [js: path="/foo.js"] ================================================ FILE: test/autotests/dependency-walker/flat/foo.js ================================================ console.log('foo'); ================================================ FILE: test/autotests/dependency-walker/flat/package.json ================================================ { "name": "autotest", "version": "1.0.0" } ================================================ FILE: test/autotests/dependency-walker/flat/test.js ================================================ exports.getDependencies = function(dir) { return [ dir + '/browser.json' ]; }; ================================================ FILE: test/autotests/dependency-walker/globs/browser.json ================================================ { "dependencies": [ "style/*.css", "require: require/*.js" ] } ================================================ FILE: test/autotests/dependency-walker/globs/expected.txt ================================================ [package: path="/browser.json"] + [css: path="/style/style1.css"] + [css: path="/style/style2.css"] [require: /require/bar.js] + [commonjs-runtime] + [commonjs-ready: inline="end"] + [commonjs-def: path="/autotest$1.0.0/require/bar"] [require: /require/foo.js] + [commonjs-def: path="/autotest$1.0.0/require/foo"] ================================================ FILE: test/autotests/dependency-walker/globs/package.json ================================================ { "name": "autotest", "version": "1.0.0" } ================================================ FILE: test/autotests/dependency-walker/globs/require/bar.js ================================================ module.exports="BAR"; ================================================ FILE: test/autotests/dependency-walker/globs/require/foo.js ================================================ module.exports="FOO"; ================================================ FILE: test/autotests/dependency-walker/globs/style/style1.css ================================================ .style1 {} ================================================ FILE: test/autotests/dependency-walker/globs/style/style2.css ================================================ .style2 {} ================================================ FILE: test/autotests/dependency-walker/globs/test.js ================================================ exports.getDependencies = function(dir) { return [ dir + '/browser.json' ]; }; ================================================ FILE: test/autotests/dependency-walker/lasso-issue-136/browser.json ================================================ { "dependencies": [ { "if-flag": "foo", "dependencies": [ "./common.js", { "path": "./foo1.js" } ] }, { "if-flag": "foo", "dependencies": [ "./common.js", { "path": "./foo2.js" } ] } ] } ================================================ FILE: test/autotests/dependency-walker/lasso-issue-136/common.js ================================================ console.log('common'); ================================================ FILE: test/autotests/dependency-walker/lasso-issue-136/expected.txt ================================================ [package: path="/browser.json"] [dependencies: if-flag="foo", dependencies=["./common.js",{"path":"./foo1.js"}]] + [js: path="/common.js"] + [js: path="/foo1.js"] [dependencies: if-flag="foo", dependencies=["./common.js",{"path":"./foo2.js"}]] + [js: path="/foo2.js"] ================================================ FILE: test/autotests/dependency-walker/lasso-issue-136/foo1.js ================================================ console.log('foo1'); ================================================ FILE: test/autotests/dependency-walker/lasso-issue-136/foo2.js ================================================ console.log('foo2'); ================================================ FILE: test/autotests/dependency-walker/lasso-issue-136/package.json ================================================ { "name": "autotest", "version": "1.0.0" } ================================================ FILE: test/autotests/dependency-walker/lasso-issue-136/test.js ================================================ exports.getFlags = function() { return ['foo']; }; exports.getDependencies = function(dir) { return [ dir + '/browser.json' ]; }; ================================================ FILE: test/autotests/dependency-walker/require/browser.json ================================================ { "dependencies": [ "require: ./foo" ] } ================================================ FILE: test/autotests/dependency-walker/require/expected.txt ================================================ [package: path="/browser.json"] [require: ./foo] + [commonjs-runtime] + [commonjs-ready: inline="end"] + [commonjs-main: dir="/autotest$1.0.0/nested", main=""] [require: ./nested] + [commonjs-def: path="/autotest$1.0.0/nested/index"] + [commonjs-def: path="/autotest$1.0.0/foo"] ================================================ FILE: test/autotests/dependency-walker/require/foo.js ================================================ require('./nested'); ================================================ FILE: test/autotests/dependency-walker/require/nested/index.js ================================================ console.log('nested'); ================================================ FILE: test/autotests/dependency-walker/require/package.json ================================================ { "name": "autotest", "version": "1.0.0" } ================================================ FILE: test/autotests/dependency-walker/require/test.js ================================================ exports.getDependencies = function(dir) { return [ dir + '/browser.json' ]; }; ================================================ FILE: test/autotests/dependency-walker/require-mixed/browser.json ================================================ { "dependencies": [ "require: ./foo", "./hello.js", "./hello.css" ] } ================================================ FILE: test/autotests/dependency-walker/require-mixed/expected.txt ================================================ [package: path="/browser.json"] [require: ./foo] + [commonjs-runtime] + [commonjs-ready: inline="end"] + [commonjs-main: dir="/autotest$1.0.0/nested", main=""] [require: ./nested] + [commonjs-def: path="/autotest$1.0.0/nested/index"] + [commonjs-def: path="/autotest$1.0.0/foo"] + [js: path="/hello.js"] + [css: path="/hello.css"] ================================================ FILE: test/autotests/dependency-walker/require-mixed/foo.js ================================================ require('./nested'); ================================================ FILE: test/autotests/dependency-walker/require-mixed/hello.css ================================================ .hello { font-weight: bold; } ================================================ FILE: test/autotests/dependency-walker/require-mixed/hello.js ================================================ console.log('Hello World'); ================================================ FILE: test/autotests/dependency-walker/require-mixed/nested/index.js ================================================ console.log('nested'); ================================================ FILE: test/autotests/dependency-walker/require-mixed/package.json ================================================ { "name": "autotest", "version": "1.0.0" } ================================================ FILE: test/autotests/dependency-walker/require-mixed/test.js ================================================ exports.getDependencies = function(dir) { return [ dir + '/browser.json' ]; }; ================================================ FILE: test/autotests/dependency-walker/transitive-package/browser.json ================================================ { "dependencies": [ "./foo.js", "./nested/browser.json" ] } ================================================ FILE: test/autotests/dependency-walker/transitive-package/expected.txt ================================================ [package: path="/browser.json"] + [js: path="/foo.js"] [package: path="/nested/browser.json"] + [js: path="/nested/bar.js"] ================================================ FILE: test/autotests/dependency-walker/transitive-package/foo.js ================================================ console.log('foo'); ================================================ FILE: test/autotests/dependency-walker/transitive-package/nested/bar.js ================================================ console.log('bar'); ================================================ FILE: test/autotests/dependency-walker/transitive-package/nested/browser.json ================================================ { "dependencies": [ "./bar.js" ] } ================================================ FILE: test/autotests/dependency-walker/transitive-package/package.json ================================================ { "name": "autotest", "version": "1.0.0" } ================================================ FILE: test/autotests/dependency-walker/transitive-package/test.js ================================================ exports.getDependencies = function(dir) { return [ dir + '/browser.json' ]; }; ================================================ FILE: test/autotests/flags/bundling-enabled/a.js ================================================ a=true; ================================================ FILE: test/autotests/flags/bundling-enabled/b.js ================================================ b=true; ================================================ FILE: test/autotests/flags/bundling-enabled/browser.json ================================================ { "dependencies": [ { "type": "js", "path": "./a.js", "if-flag": "a" }, { "type": "js", "path": "./b.js", "if-flag": "b" }, { "type": "js", "path": "./c.js" }, { "if-flag": "mobile", "dependencies": [ "./mobile-a.js", "./mobile-b.js" ] }, { "if-not-flag": "mobile", "dependencies": [ "./desktop-a.js", "./desktop-b.js" ] } ] } ================================================ FILE: test/autotests/flags/bundling-enabled/c.js ================================================ c=true; ================================================ FILE: test/autotests/flags/bundling-enabled/desktop-a.js ================================================ desktop_a ================================================ FILE: test/autotests/flags/bundling-enabled/desktop-b.js ================================================ desktop_b ================================================ FILE: test/autotests/flags/bundling-enabled/mobile-a.js ================================================ mobile_a ================================================ FILE: test/autotests/flags/bundling-enabled/mobile-b.js ================================================ mobile_b ================================================ FILE: test/autotests/flags/bundling-enabled/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/flags/bundling-enabled/test.js ================================================ var expect = require('chai').expect; exports.getLassoConfig = function() { return { fingerprintsEnabled: false, flags: ['a'], bundlingEnabled: true, bundles: [ { name: 'foo', dependencies: [ './browser.json' ] } ] }; }; exports.getLassoOptions = function() { return { dependencies: [ './browser.json' ] }; }; exports.check = function(lassoPageResult, writerTracker) { var firstFile = lassoPageResult.getJavaScriptFiles()[0]; expect(writerTracker.getCodeForPath(firstFile)).to.equal('a=true;\nc=true;'); }; ================================================ FILE: test/autotests/flags/if-flag-dependencies/a.js ================================================ a=true; ================================================ FILE: test/autotests/flags/if-flag-dependencies/b.js ================================================ b=true; ================================================ FILE: test/autotests/flags/if-flag-dependencies/browser.json ================================================ { "dependencies": [ { "type": "js", "path": "./a.js", "if-flag": "a" }, { "type": "js", "path": "./b.js", "if-flag": "b" }, { "type": "js", "path": "./c.js" }, { "if-flag": "mobile", "dependencies": [ "./mobile-a.js", "./mobile-b.js" ] }, { "if-not-flag": "mobile", "dependencies": [ "./desktop-a.js", "./desktop-b.js" ] } ] } ================================================ FILE: test/autotests/flags/if-flag-dependencies/c.js ================================================ c=true; ================================================ FILE: test/autotests/flags/if-flag-dependencies/desktop-a.js ================================================ desktop_a ================================================ FILE: test/autotests/flags/if-flag-dependencies/desktop-b.js ================================================ desktop_b ================================================ FILE: test/autotests/flags/if-flag-dependencies/mobile-a.js ================================================ mobile_a ================================================ FILE: test/autotests/flags/if-flag-dependencies/mobile-b.js ================================================ mobile_b ================================================ FILE: test/autotests/flags/if-flag-dependencies/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/flags/if-flag-dependencies/test.js ================================================ var expect = require('chai').expect; exports.getLassoConfig = function() { return { fingerprintsEnabled: false, flags: ['mobile'], bundlingEnabled: false }; }; exports.getLassoOptions = function() { return { dependencies: [ './browser.json' ] }; }; exports.check = function(lassoPageResult, writerTracker) { expect(writerTracker.getOutputFilenames()).to.deep.equal([ // a.js only included if "a" flag is enabled //'a.js', /* NOTE: b.js should not be included because it requires flag "b" */ // 'a.js' // c.js is always included (not conditional) 'c.js', 'mobile-a.js', 'mobile-b.js' ]); expect(writerTracker.getCodeForFilename('c.js')).to.equal('c=true;'); expect(writerTracker.getCodeForFilename('mobile-a.js')).to.equal('mobile_a'); expect(writerTracker.getCodeForFilename('mobile-b.js')).to.equal('mobile_b'); }; ================================================ FILE: test/autotests/flags/if-not-flag/a.js ================================================ a=true; ================================================ FILE: test/autotests/flags/if-not-flag/b.js ================================================ b=true; ================================================ FILE: test/autotests/flags/if-not-flag/browser.json ================================================ { "dependencies": [ { "type": "js", "path": "./a.js", "if-flag": "a" }, { "type": "js", "path": "./b.js", "if-flag": "b" }, { "type": "js", "path": "./c.js" }, { "if-flag": "mobile", "dependencies": [ "./mobile-a.js", "./mobile-b.js" ] }, { "if-not-flag": "mobile", "dependencies": [ "./desktop-a.js", "./desktop-b.js" ] } ] } ================================================ FILE: test/autotests/flags/if-not-flag/c.js ================================================ c=true; ================================================ FILE: test/autotests/flags/if-not-flag/desktop-a.js ================================================ desktop_a ================================================ FILE: test/autotests/flags/if-not-flag/desktop-b.js ================================================ desktop_b ================================================ FILE: test/autotests/flags/if-not-flag/mobile-a.js ================================================ mobile_a ================================================ FILE: test/autotests/flags/if-not-flag/mobile-b.js ================================================ mobile_b ================================================ FILE: test/autotests/flags/if-not-flag/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/flags/if-not-flag/test.js ================================================ var expect = require('chai').expect; exports.getLassoConfig = function() { return { fingerprintsEnabled: false, flags: ['a'], bundlingEnabled: false }; }; exports.getLassoOptions = function() { return { dependencies: [ {'type': 'js', 'path': './a.js', 'if-flag': 'a'}, {'type': 'js', 'path': './b.js', 'if-not-flag': 'a'} ] }; }; exports.check = function(lassoPageResult, writerTracker) { expect(writerTracker.getOutputFilenames()).to.deep.equal([ // a.js only included if "a" flag is enabled 'a.js', /* NOTE: b.js should not be included because it will only be included if "a" flag is not enabled */ // 'b.js' ]); expect(writerTracker.getCodeForFilename('a.js')).to.equal('a=true;'); expect(writerTracker.getCodeForFilename('b.js')).to.equal(undefined); }; ================================================ FILE: test/autotests/flags/long-flags/a.js ================================================ a=true; ================================================ FILE: test/autotests/flags/long-flags/browser.json ================================================ { "dependencies": [ { "type": "js", "path": "./a.js", "if-flag": "a" } ] } ================================================ FILE: test/autotests/flags/long-flags/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/flags/long-flags/test.js ================================================ var expect = require('chai').expect; exports.getLassoConfig = function() { return { fingerprintsEnabled: false, flags: [ 'a', 'jldksjflksajdflsakjflskadfjkjdsafhksdjfhkfdgd', 'dfsgsgsgsdfgdsgfdgssdfgadfjkjdsafhksdjfhkdjsf', 'qwfddfdgsfdklkkfdgssdfgadfjkjdsafhkfdgdsgdsdd', 'uytursgsgsgsdfgdsgfdiusaiuvuefvdsbgoinkdcnoia', 'xnioenoiwihfuaevfsmoaeuavrhgbdjsnkmkmoieyabsb', 'sopoiresjhvdgasmasofniewrubyadfknjankndsfkjnk', 'hhabebherbsjbndkjserwoijwnbhewrfuwherfuewfknd' ], bundlingEnabled: false }; }; exports.getLassoOptions = function() { return { dependencies: [ './browser.json' ] }; }; exports.check = function(lassoPageResult, writerTracker) { // if we made it here, all is good. }; ================================================ FILE: test/autotests/flags/simple/a.js ================================================ a=true; ================================================ FILE: test/autotests/flags/simple/b.js ================================================ b=true; ================================================ FILE: test/autotests/flags/simple/browser.json ================================================ { "dependencies": [ { "type": "js", "path": "./a.js", "if-flag": "a" }, { "type": "js", "path": "./b.js", "if-flag": "b" }, { "type": "js", "path": "./c.js" }, { "if-flag": "mobile", "dependencies": [ "./mobile-a.js", "./mobile-b.js" ] }, { "if-not-flag": "mobile", "dependencies": [ "./desktop-a.js", "./desktop-b.js" ] } ] } ================================================ FILE: test/autotests/flags/simple/c.js ================================================ c=true; ================================================ FILE: test/autotests/flags/simple/desktop-a.js ================================================ desktop_a ================================================ FILE: test/autotests/flags/simple/desktop-b.js ================================================ desktop_b ================================================ FILE: test/autotests/flags/simple/mobile-a.js ================================================ mobile_a ================================================ FILE: test/autotests/flags/simple/mobile-b.js ================================================ mobile_b ================================================ FILE: test/autotests/flags/simple/package.json ================================================ { "name": "test-bundling-project", "version": "0.0.0" } ================================================ FILE: test/autotests/flags/simple/test.js ================================================ var expect = require('chai').expect; exports.getLassoConfig = function() { return { fingerprintsEnabled: false, flags: ['a'], bundlingEnabled: false }; }; exports.getLassoOptions = function() { return { dependencies: [ './browser.json' ] }; }; exports.check = function(lassoPageResult, writerTracker) { expect(writerTracker.getOutputFilenames()).to.deep.equal([ // a.js only included if "a" flag is enabled 'a.js', /* NOTE: b.js should not be included because it requires flag "b" */ // 'a.js' // c.js is always included (not conditional) 'c.js', 'desktop-a.js', 'desktop-b.js' ]); expect(writerTracker.getCodeForFilename('a.js')).to.equal('a=true;'); expect(writerTracker.getCodeForFilename('c.js')).to.equal('c=true;'); expect(writerTracker.getCodeForFilename('desktop-a.js')).to.equal('desktop_a'); expect(writerTracker.getCodeForFilename('desktop-b.js')).to.equal('desktop_b'); }; ================================================ FILE: test/autotests/inspect/buffer/expected.json ================================================ { "requires": [ { "path": "base64-js", "range": [ 211, 231 ], "argRange": [ 219, 230 ] }, { "path": "ieee754", "range": [ 246, 264 ], "argRange": [ 254, 263 ] }, { "path": "isarray", "range": [ 279, 297 ], "argRange": [ 287, 296 ] } ], "foundGlobals": { "global": true, "undefined": true, "Uint8Array": true, "e": true, "TypeError": true, "ArrayBuffer": true, "Symbol": true, "Object": true, "RangeError": true, "Math": true, "String": true, "Infinity": true, "Number": true, "Error": true, "parseInt": true, "isNaN": true, "isFinite": true, "Array": true }, "asyncBlocks": [] } ================================================ FILE: test/autotests/inspect/buffer/input.js ================================================ /*! * The buffer module from node.js, for the browser. * * @author Feross Aboukhadijeh * @license MIT */ /* eslint-disable no-proto */ 'use strict' var base64 = require('base64-js') var ieee754 = require('ieee754') var isArray = require('isarray') exports.Buffer = Buffer exports.SlowBuffer = SlowBuffer exports.INSPECT_MAX_BYTES = 50 Buffer.poolSize = 8192 // not used by this implementation var rootParent = {} /** * If `Buffer.TYPED_ARRAY_SUPPORT`: * === true Use Uint8Array implementation (fastest) * === false Use Object implementation (most compatible, even IE6) * * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+, * Opera 11.6+, iOS 4.2+. * * Due to various browser bugs, sometimes the Object implementation will be used even * when the browser supports typed arrays. * * Note: * * - Firefox 4-29 lacks support for adding new properties to `Uint8Array` instances, * See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438. * * - Chrome 9-10 is missing the `TypedArray.prototype.subarray` function. * * - IE10 has a broken `TypedArray.prototype.subarray` function which returns arrays of * incorrect length in some situations. * We detect these buggy browsers and set `Buffer.TYPED_ARRAY_SUPPORT` to `false` so they * get the Object implementation, which is slower but behaves correctly. */ Buffer.TYPED_ARRAY_SUPPORT = global.TYPED_ARRAY_SUPPORT !== undefined ? global.TYPED_ARRAY_SUPPORT : typedArraySupport() function typedArraySupport () { try { var arr = new Uint8Array(1) arr.foo = function () { return 42 } return arr.foo() === 42 && // typed array instances can be augmented typeof arr.subarray === 'function' && // chrome 9-10 lack `subarray` arr.subarray(1, 1).byteLength === 0 // ie10 has broken `subarray` } catch (e) { return false } } function kMaxLength () { return Buffer.TYPED_ARRAY_SUPPORT ? 0x7fffffff : 0x3fffffff } /** * The Buffer constructor returns instances of `Uint8Array` that have their * prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of * `Uint8Array`, so the returned instances will have all the node `Buffer` methods * and the `Uint8Array` methods. Square bracket notation works as expected -- it * returns a single octet. * * The `Uint8Array` prototype remains unmodified. */ function Buffer (arg) { if (!(this instanceof Buffer)) { // Avoid going through an ArgumentsAdaptorTrampoline in the common case. if (arguments.length > 1) return new Buffer(arg, arguments[1]) return new Buffer(arg) } if (!Buffer.TYPED_ARRAY_SUPPORT) { this.length = 0 this.parent = undefined } // Common case. if (typeof arg === 'number') { return fromNumber(this, arg) } // Slightly less common case. if (typeof arg === 'string') { return fromString(this, arg, arguments.length > 1 ? arguments[1] : 'utf8') } // Unusual. return fromObject(this, arg) } // TODO: Legacy, not needed anymore. Remove in next major version. Buffer._augment = function (arr) { arr.__proto__ = Buffer.prototype return arr } function fromNumber (that, length) { that = allocate(that, length < 0 ? 0 : checked(length) | 0) if (!Buffer.TYPED_ARRAY_SUPPORT) { for (var i = 0; i < length; i++) { that[i] = 0 } } return that } function fromString (that, string, encoding) { if (typeof encoding !== 'string' || encoding === '') encoding = 'utf8' // Assumption: byteLength() return value is always < kMaxLength. var length = byteLength(string, encoding) | 0 that = allocate(that, length) that.write(string, encoding) return that } function fromObject (that, object) { if (Buffer.isBuffer(object)) return fromBuffer(that, object) if (isArray(object)) return fromArray(that, object) if (object == null) { throw new TypeError('must start with number, buffer, array or string') } if (typeof ArrayBuffer !== 'undefined') { if (object.buffer instanceof ArrayBuffer) { return fromTypedArray(that, object) } if (object instanceof ArrayBuffer) { return fromArrayBuffer(that, object) } } if (object.length) return fromArrayLike(that, object) return fromJsonObject(that, object) } function fromBuffer (that, buffer) { var length = checked(buffer.length) | 0 that = allocate(that, length) buffer.copy(that, 0, 0, length) return that } function fromArray (that, array) { var length = checked(array.length) | 0 that = allocate(that, length) for (var i = 0; i < length; i += 1) { that[i] = array[i] & 255 } return that } // Duplicate of fromArray() to keep fromArray() monomorphic. function fromTypedArray (that, array) { var length = checked(array.length) | 0 that = allocate(that, length) // Truncating the elements is probably not what people expect from typed // arrays with BYTES_PER_ELEMENT > 1 but it's compatible with the behavior // of the old Buffer constructor. for (var i = 0; i < length; i += 1) { that[i] = array[i] & 255 } return that } function fromArrayBuffer (that, array) { array.byteLength // this throws if `array` is not a valid ArrayBuffer if (Buffer.TYPED_ARRAY_SUPPORT) { // Return an augmented `Uint8Array` instance, for best performance that = new Uint8Array(array) that.__proto__ = Buffer.prototype } else { // Fallback: Return an object instance of the Buffer class that = fromTypedArray(that, new Uint8Array(array)) } return that } function fromArrayLike (that, array) { var length = checked(array.length) | 0 that = allocate(that, length) for (var i = 0; i < length; i += 1) { that[i] = array[i] & 255 } return that } // Deserialize { type: 'Buffer', data: [1,2,3,...] } into a Buffer object. // Returns a zero-length buffer for inputs that don't conform to the spec. function fromJsonObject (that, object) { var array var length = 0 if (object.type === 'Buffer' && isArray(object.data)) { array = object.data length = checked(array.length) | 0 } that = allocate(that, length) for (var i = 0; i < length; i += 1) { that[i] = array[i] & 255 } return that } if (Buffer.TYPED_ARRAY_SUPPORT) { Buffer.prototype.__proto__ = Uint8Array.prototype Buffer.__proto__ = Uint8Array if (typeof Symbol !== 'undefined' && Symbol.species && Buffer[Symbol.species] === Buffer) { // Fix subarray() in ES2016. See: https://github.com/feross/buffer/pull/97 Object.defineProperty(Buffer, Symbol.species, { value: null, configurable: true }) } } else { // pre-set for values that may exist in the future Buffer.prototype.length = undefined Buffer.prototype.parent = undefined } function allocate (that, length) { if (Buffer.TYPED_ARRAY_SUPPORT) { // Return an augmented `Uint8Array` instance, for best performance that = new Uint8Array(length) that.__proto__ = Buffer.prototype } else { // Fallback: Return an object instance of the Buffer class that.length = length } var fromPool = length !== 0 && length <= Buffer.poolSize >>> 1 if (fromPool) that.parent = rootParent return that } function checked (length) { // Note: cannot use `length < kMaxLength` here because that fails when // length is NaN (which is otherwise coerced to zero.) if (length >= kMaxLength()) { throw new RangeError('Attempt to allocate Buffer larger than maximum ' + 'size: 0x' + kMaxLength().toString(16) + ' bytes') } return length | 0 } function SlowBuffer (subject, encoding) { if (!(this instanceof SlowBuffer)) return new SlowBuffer(subject, encoding) var buf = new Buffer(subject, encoding) delete buf.parent return buf } Buffer.isBuffer = function isBuffer (b) { return !!(b != null && b._isBuffer) } Buffer.compare = function compare (a, b) { if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) { throw new TypeError('Arguments must be Buffers') } if (a === b) return 0 var x = a.length var y = b.length for (var i = 0, len = Math.min(x, y); i < len; ++i) { if (a[i] !== b[i]) { x = a[i] y = b[i] break } } if (x < y) return -1 if (y < x) return 1 return 0 } Buffer.isEncoding = function isEncoding (encoding) { switch (String(encoding).toLowerCase()) { case 'hex': case 'utf8': case 'utf-8': case 'ascii': case 'binary': case 'base64': case 'raw': case 'ucs2': case 'ucs-2': case 'utf16le': case 'utf-16le': return true default: return false } } Buffer.concat = function concat (list, length) { if (!isArray(list)) throw new TypeError('list argument must be an Array of Buffers.') if (list.length === 0) { return new Buffer(0) } var i if (length === undefined) { length = 0 for (i = 0; i < list.length; i++) { length += list[i].length } } var buf = new Buffer(length) var pos = 0 for (i = 0; i < list.length; i++) { var item = list[i] item.copy(buf, pos) pos += item.length } return buf } function byteLength (string, encoding) { if (typeof string !== 'string') string = '' + string var len = string.length if (len === 0) return 0 // Use a for loop to avoid recursion var loweredCase = false for (;;) { switch (encoding) { case 'ascii': case 'binary': // Deprecated case 'raw': case 'raws': return len case 'utf8': case 'utf-8': return utf8ToBytes(string).length case 'ucs2': case 'ucs-2': case 'utf16le': case 'utf-16le': return len * 2 case 'hex': return len >>> 1 case 'base64': return base64ToBytes(string).length default: if (loweredCase) return utf8ToBytes(string).length // assume utf8 encoding = ('' + encoding).toLowerCase() loweredCase = true } } } Buffer.byteLength = byteLength function slowToString (encoding, start, end) { var loweredCase = false start = start | 0 end = end === undefined || end === Infinity ? this.length : end | 0 if (!encoding) encoding = 'utf8' if (start < 0) start = 0 if (end > this.length) end = this.length if (end <= start) return '' while (true) { switch (encoding) { case 'hex': return hexSlice(this, start, end) case 'utf8': case 'utf-8': return utf8Slice(this, start, end) case 'ascii': return asciiSlice(this, start, end) case 'binary': return binarySlice(this, start, end) case 'base64': return base64Slice(this, start, end) case 'ucs2': case 'ucs-2': case 'utf16le': case 'utf-16le': return utf16leSlice(this, start, end) default: if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) encoding = (encoding + '').toLowerCase() loweredCase = true } } } // The property is used by `Buffer.isBuffer` and `is-buffer` (in Safari 5-7) to detect // Buffer instances. Buffer.prototype._isBuffer = true Buffer.prototype.toString = function toString () { var length = this.length | 0 if (length === 0) return '' if (arguments.length === 0) return utf8Slice(this, 0, length) return slowToString.apply(this, arguments) } Buffer.prototype.equals = function equals (b) { if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer') if (this === b) return true return Buffer.compare(this, b) === 0 } Buffer.prototype.inspect = function inspect () { var str = '' var max = exports.INSPECT_MAX_BYTES if (this.length > 0) { str = this.toString('hex', 0, max).match(/.{2}/g).join(' ') if (this.length > max) str += ' ... ' } return '' } Buffer.prototype.compare = function compare (b) { if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer') return Buffer.compare(this, b) } Buffer.prototype.indexOf = function indexOf (val, byteOffset) { if (byteOffset > 0x7fffffff) byteOffset = 0x7fffffff else if (byteOffset < -0x80000000) byteOffset = -0x80000000 byteOffset >>= 0 if (this.length === 0) return -1 if (byteOffset >= this.length) return -1 // Negative offsets start from the end of the buffer if (byteOffset < 0) byteOffset = Math.max(this.length + byteOffset, 0) if (typeof val === 'string') { if (val.length === 0) return -1 // special case: looking for empty string always fails return String.prototype.indexOf.call(this, val, byteOffset) } if (Buffer.isBuffer(val)) { return arrayIndexOf(this, val, byteOffset) } if (typeof val === 'number') { if (Buffer.TYPED_ARRAY_SUPPORT && Uint8Array.prototype.indexOf === 'function') { return Uint8Array.prototype.indexOf.call(this, val, byteOffset) } return arrayIndexOf(this, [ val ], byteOffset) } function arrayIndexOf (arr, val, byteOffset) { var foundIndex = -1 for (var i = 0; byteOffset + i < arr.length; i++) { if (arr[byteOffset + i] === val[foundIndex === -1 ? 0 : i - foundIndex]) { if (foundIndex === -1) foundIndex = i if (i - foundIndex + 1 === val.length) return byteOffset + foundIndex } else { foundIndex = -1 } } return -1 } throw new TypeError('val must be string, number or Buffer') } function hexWrite (buf, string, offset, length) { offset = Number(offset) || 0 var remaining = buf.length - offset if (!length) { length = remaining } else { length = Number(length) if (length > remaining) { length = remaining } } // must be an even number of digits var strLen = string.length if (strLen % 2 !== 0) throw new Error('Invalid hex string') if (length > strLen / 2) { length = strLen / 2 } for (var i = 0; i < length; i++) { var parsed = parseInt(string.substr(i * 2, 2), 16) if (isNaN(parsed)) throw new Error('Invalid hex string') buf[offset + i] = parsed } return i } function utf8Write (buf, string, offset, length) { return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length) } function asciiWrite (buf, string, offset, length) { return blitBuffer(asciiToBytes(string), buf, offset, length) } function binaryWrite (buf, string, offset, length) { return asciiWrite(buf, string, offset, length) } function base64Write (buf, string, offset, length) { return blitBuffer(base64ToBytes(string), buf, offset, length) } function ucs2Write (buf, string, offset, length) { return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length) } Buffer.prototype.write = function write (string, offset, length, encoding) { // Buffer#write(string) if (offset === undefined) { encoding = 'utf8' length = this.length offset = 0 // Buffer#write(string, encoding) } else if (length === undefined && typeof offset === 'string') { encoding = offset length = this.length offset = 0 // Buffer#write(string, offset[, length][, encoding]) } else if (isFinite(offset)) { offset = offset | 0 if (isFinite(length)) { length = length | 0 if (encoding === undefined) encoding = 'utf8' } else { encoding = length length = undefined } // legacy write(string, encoding, offset, length) - remove in v0.13 } else { var swap = encoding encoding = offset offset = length | 0 length = swap } var remaining = this.length - offset if (length === undefined || length > remaining) length = remaining if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) { throw new RangeError('attempt to write outside buffer bounds') } if (!encoding) encoding = 'utf8' var loweredCase = false for (;;) { switch (encoding) { case 'hex': return hexWrite(this, string, offset, length) case 'utf8': case 'utf-8': return utf8Write(this, string, offset, length) case 'ascii': return asciiWrite(this, string, offset, length) case 'binary': return binaryWrite(this, string, offset, length) case 'base64': // Warning: maxLength not taken into account in base64Write return base64Write(this, string, offset, length) case 'ucs2': case 'ucs-2': case 'utf16le': case 'utf-16le': return ucs2Write(this, string, offset, length) default: if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding) encoding = ('' + encoding).toLowerCase() loweredCase = true } } } Buffer.prototype.toJSON = function toJSON () { return { type: 'Buffer', data: Array.prototype.slice.call(this._arr || this, 0) } } function base64Slice (buf, start, end) { if (start === 0 && end === buf.length) { return base64.fromByteArray(buf) } else { return base64.fromByteArray(buf.slice(start, end)) } } function utf8Slice (buf, start, end) { end = Math.min(buf.length, end) var res = [] var i = start while (i < end) { var firstByte = buf[i] var codePoint = null var bytesPerSequence = (firstByte > 0xEF) ? 4 : (firstByte > 0xDF) ? 3 : (firstByte > 0xBF) ? 2 : 1 if (i + bytesPerSequence <= end) { var secondByte, thirdByte, fourthByte, tempCodePoint switch (bytesPerSequence) { case 1: if (firstByte < 0x80) { codePoint = firstByte } break case 2: secondByte = buf[i + 1] if ((secondByte & 0xC0) === 0x80) { tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F) if (tempCodePoint > 0x7F) { codePoint = tempCodePoint } } break case 3: secondByte = buf[i + 1] thirdByte = buf[i + 2] if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) { tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F) if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) { codePoint = tempCodePoint } } break case 4: secondByte = buf[i + 1] thirdByte = buf[i + 2] fourthByte = buf[i + 3] if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) { tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F) if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) { codePoint = tempCodePoint } } } } if (codePoint === null) { // we did not generate a valid codePoint so insert a // replacement char (U+FFFD) and advance only 1 byte codePoint = 0xFFFD bytesPerSequence = 1 } else if (codePoint > 0xFFFF) { // encode to utf16 (surrogate pair dance) codePoint -= 0x10000 res.push(codePoint >>> 10 & 0x3FF | 0xD800) codePoint = 0xDC00 | codePoint & 0x3FF } res.push(codePoint) i += bytesPerSequence } return decodeCodePointsArray(res) } // Based on http://stackoverflow.com/a/22747272/680742, the browser with // the lowest limit is Chrome, with 0x10000 args. // We go 1 magnitude less, for safety var MAX_ARGUMENTS_LENGTH = 0x1000 function decodeCodePointsArray (codePoints) { var len = codePoints.length if (len <= MAX_ARGUMENTS_LENGTH) { return String.fromCharCode.apply(String, codePoints) // avoid extra slice() } // Decode in chunks to avoid "call stack size exceeded". var res = '' var i = 0 while (i < len) { res += String.fromCharCode.apply( String, codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH) ) } return res } function asciiSlice (buf, start, end) { var ret = '' end = Math.min(buf.length, end) for (var i = start; i < end; i++) { ret += String.fromCharCode(buf[i] & 0x7F) } return ret } function binarySlice (buf, start, end) { var ret = '' end = Math.min(buf.length, end) for (var i = start; i < end; i++) { ret += String.fromCharCode(buf[i]) } return ret } function hexSlice (buf, start, end) { var len = buf.length if (!start || start < 0) start = 0 if (!end || end < 0 || end > len) end = len var out = '' for (var i = start; i < end; i++) { out += toHex(buf[i]) } return out } function utf16leSlice (buf, start, end) { var bytes = buf.slice(start, end) var res = '' for (var i = 0; i < bytes.length; i += 2) { res += String.fromCharCode(bytes[i] + bytes[i + 1] * 256) } return res } Buffer.prototype.slice = function slice (start, end) { var len = this.length start = ~~start end = end === undefined ? len : ~~end if (start < 0) { start += len if (start < 0) start = 0 } else if (start > len) { start = len } if (end < 0) { end += len if (end < 0) end = 0 } else if (end > len) { end = len } if (end < start) end = start var newBuf if (Buffer.TYPED_ARRAY_SUPPORT) { newBuf = this.subarray(start, end) newBuf.__proto__ = Buffer.prototype } else { var sliceLen = end - start newBuf = new Buffer(sliceLen, undefined) for (var i = 0; i < sliceLen; i++) { newBuf[i] = this[i + start] } } if (newBuf.length) newBuf.parent = this.parent || this return newBuf } /* * Need to make sure that buffer isn't trying to write out of bounds. */ function checkOffset (offset, ext, length) { if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint') if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length') } Buffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) { offset = offset | 0 byteLength = byteLength | 0 if (!noAssert) checkOffset(offset, byteLength, this.length) var val = this[offset] var mul = 1 var i = 0 while (++i < byteLength && (mul *= 0x100)) { val += this[offset + i] * mul } return val } Buffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) { offset = offset | 0 byteLength = byteLength | 0 if (!noAssert) { checkOffset(offset, byteLength, this.length) } var val = this[offset + --byteLength] var mul = 1 while (byteLength > 0 && (mul *= 0x100)) { val += this[offset + --byteLength] * mul } return val } Buffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) { if (!noAssert) checkOffset(offset, 1, this.length) return this[offset] } Buffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) { if (!noAssert) checkOffset(offset, 2, this.length) return this[offset] | (this[offset + 1] << 8) } Buffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) { if (!noAssert) checkOffset(offset, 2, this.length) return (this[offset] << 8) | this[offset + 1] } Buffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) { if (!noAssert) checkOffset(offset, 4, this.length) return ((this[offset]) | (this[offset + 1] << 8) | (this[offset + 2] << 16)) + (this[offset + 3] * 0x1000000) } Buffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) { if (!noAssert) checkOffset(offset, 4, this.length) return (this[offset] * 0x1000000) + ((this[offset + 1] << 16) | (this[offset + 2] << 8) | this[offset + 3]) } Buffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) { offset = offset | 0 byteLength = byteLength | 0 if (!noAssert) checkOffset(offset, byteLength, this.length) var val = this[offset] var mul = 1 var i = 0 while (++i < byteLength && (mul *= 0x100)) { val += this[offset + i] * mul } mul *= 0x80 if (val >= mul) val -= Math.pow(2, 8 * byteLength) return val } Buffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) { offset = offset | 0 byteLength = byteLength | 0 if (!noAssert) checkOffset(offset, byteLength, this.length) var i = byteLength var mul = 1 var val = this[offset + --i] while (i > 0 && (mul *= 0x100)) { val += this[offset + --i] * mul } mul *= 0x80 if (val >= mul) val -= Math.pow(2, 8 * byteLength) return val } Buffer.prototype.readInt8 = function readInt8 (offset, noAssert) { if (!noAssert) checkOffset(offset, 1, this.length) if (!(this[offset] & 0x80)) return (this[offset]) return ((0xff - this[offset] + 1) * -1) } Buffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) { if (!noAssert) checkOffset(offset, 2, this.length) var val = this[offset] | (this[offset + 1] << 8) return (val & 0x8000) ? val | 0xFFFF0000 : val } Buffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) { if (!noAssert) checkOffset(offset, 2, this.length) var val = this[offset + 1] | (this[offset] << 8) return (val & 0x8000) ? val | 0xFFFF0000 : val } Buffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) { if (!noAssert) checkOffset(offset, 4, this.length) return (this[offset]) | (this[offset + 1] << 8) | (this[offset + 2] << 16) | (this[offset + 3] << 24) } Buffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) { if (!noAssert) checkOffset(offset, 4, this.length) return (this[offset] << 24) | (this[offset + 1] << 16) | (this[offset + 2] << 8) | (this[offset + 3]) } Buffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) { if (!noAssert) checkOffset(offset, 4, this.length) return ieee754.read(this, offset, true, 23, 4) } Buffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) { if (!noAssert) checkOffset(offset, 4, this.length) return ieee754.read(this, offset, false, 23, 4) } Buffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) { if (!noAssert) checkOffset(offset, 8, this.length) return ieee754.read(this, offset, true, 52, 8) } Buffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) { if (!noAssert) checkOffset(offset, 8, this.length) return ieee754.read(this, offset, false, 52, 8) } function checkInt (buf, value, offset, ext, max, min) { if (!Buffer.isBuffer(buf)) throw new TypeError('buffer must be a Buffer instance') if (value > max || value < min) throw new RangeError('value is out of bounds') if (offset + ext > buf.length) throw new RangeError('index out of range') } Buffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) { value = +value offset = offset | 0 byteLength = byteLength | 0 if (!noAssert) checkInt(this, value, offset, byteLength, Math.pow(2, 8 * byteLength), 0) var mul = 1 var i = 0 this[offset] = value & 0xFF while (++i < byteLength && (mul *= 0x100)) { this[offset + i] = (value / mul) & 0xFF } return offset + byteLength } Buffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) { value = +value offset = offset | 0 byteLength = byteLength | 0 if (!noAssert) checkInt(this, value, offset, byteLength, Math.pow(2, 8 * byteLength), 0) var i = byteLength - 1 var mul = 1 this[offset + i] = value & 0xFF while (--i >= 0 && (mul *= 0x100)) { this[offset + i] = (value / mul) & 0xFF } return offset + byteLength } Buffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) { value = +value offset = offset | 0 if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0) if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value) this[offset] = (value & 0xff) return offset + 1 } function objectWriteUInt16 (buf, value, offset, littleEndian) { if (value < 0) value = 0xffff + value + 1 for (var i = 0, j = Math.min(buf.length - offset, 2); i < j; i++) { buf[offset + i] = (value & (0xff << (8 * (littleEndian ? i : 1 - i)))) >>> (littleEndian ? i : 1 - i) * 8 } } Buffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) { value = +value offset = offset | 0 if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) if (Buffer.TYPED_ARRAY_SUPPORT) { this[offset] = (value & 0xff) this[offset + 1] = (value >>> 8) } else { objectWriteUInt16(this, value, offset, true) } return offset + 2 } Buffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) { value = +value offset = offset | 0 if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0) if (Buffer.TYPED_ARRAY_SUPPORT) { this[offset] = (value >>> 8) this[offset + 1] = (value & 0xff) } else { objectWriteUInt16(this, value, offset, false) } return offset + 2 } function objectWriteUInt32 (buf, value, offset, littleEndian) { if (value < 0) value = 0xffffffff + value + 1 for (var i = 0, j = Math.min(buf.length - offset, 4); i < j; i++) { buf[offset + i] = (value >>> (littleEndian ? i : 3 - i) * 8) & 0xff } } Buffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) { value = +value offset = offset | 0 if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) if (Buffer.TYPED_ARRAY_SUPPORT) { this[offset + 3] = (value >>> 24) this[offset + 2] = (value >>> 16) this[offset + 1] = (value >>> 8) this[offset] = (value & 0xff) } else { objectWriteUInt32(this, value, offset, true) } return offset + 4 } Buffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) { value = +value offset = offset | 0 if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0) if (Buffer.TYPED_ARRAY_SUPPORT) { this[offset] = (value >>> 24) this[offset + 1] = (value >>> 16) this[offset + 2] = (value >>> 8) this[offset + 3] = (value & 0xff) } else { objectWriteUInt32(this, value, offset, false) } return offset + 4 } Buffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) { value = +value offset = offset | 0 if (!noAssert) { var limit = Math.pow(2, 8 * byteLength - 1) checkInt(this, value, offset, byteLength, limit - 1, -limit) } var i = 0 var mul = 1 var sub = value < 0 ? 1 : 0 this[offset] = value & 0xFF while (++i < byteLength && (mul *= 0x100)) { this[offset + i] = ((value / mul) >> 0) - sub & 0xFF } return offset + byteLength } Buffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) { value = +value offset = offset | 0 if (!noAssert) { var limit = Math.pow(2, 8 * byteLength - 1) checkInt(this, value, offset, byteLength, limit - 1, -limit) } var i = byteLength - 1 var mul = 1 var sub = value < 0 ? 1 : 0 this[offset + i] = value & 0xFF while (--i >= 0 && (mul *= 0x100)) { this[offset + i] = ((value / mul) >> 0) - sub & 0xFF } return offset + byteLength } Buffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) { value = +value offset = offset | 0 if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80) if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value) if (value < 0) value = 0xff + value + 1 this[offset] = (value & 0xff) return offset + 1 } Buffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) { value = +value offset = offset | 0 if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) if (Buffer.TYPED_ARRAY_SUPPORT) { this[offset] = (value & 0xff) this[offset + 1] = (value >>> 8) } else { objectWriteUInt16(this, value, offset, true) } return offset + 2 } Buffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) { value = +value offset = offset | 0 if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000) if (Buffer.TYPED_ARRAY_SUPPORT) { this[offset] = (value >>> 8) this[offset + 1] = (value & 0xff) } else { objectWriteUInt16(this, value, offset, false) } return offset + 2 } Buffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) { value = +value offset = offset | 0 if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) if (Buffer.TYPED_ARRAY_SUPPORT) { this[offset] = (value & 0xff) this[offset + 1] = (value >>> 8) this[offset + 2] = (value >>> 16) this[offset + 3] = (value >>> 24) } else { objectWriteUInt32(this, value, offset, true) } return offset + 4 } Buffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) { value = +value offset = offset | 0 if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000) if (value < 0) value = 0xffffffff + value + 1 if (Buffer.TYPED_ARRAY_SUPPORT) { this[offset] = (value >>> 24) this[offset + 1] = (value >>> 16) this[offset + 2] = (value >>> 8) this[offset + 3] = (value & 0xff) } else { objectWriteUInt32(this, value, offset, false) } return offset + 4 } function checkIEEE754 (buf, value, offset, ext, max, min) { if (offset + ext > buf.length) throw new RangeError('index out of range') if (offset < 0) throw new RangeError('index out of range') } function writeFloat (buf, value, offset, littleEndian, noAssert) { if (!noAssert) { checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38) } ieee754.write(buf, value, offset, littleEndian, 23, 4) return offset + 4 } Buffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) { return writeFloat(this, value, offset, true, noAssert) } Buffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) { return writeFloat(this, value, offset, false, noAssert) } function writeDouble (buf, value, offset, littleEndian, noAssert) { if (!noAssert) { checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308) } ieee754.write(buf, value, offset, littleEndian, 52, 8) return offset + 8 } Buffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) { return writeDouble(this, value, offset, true, noAssert) } Buffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) { return writeDouble(this, value, offset, false, noAssert) } // copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length) Buffer.prototype.copy = function copy (target, targetStart, start, end) { if (!start) start = 0 if (!end && end !== 0) end = this.length if (targetStart >= target.length) targetStart = target.length if (!targetStart) targetStart = 0 if (end > 0 && end < start) end = start // Copy 0 bytes; we're done if (end === start) return 0 if (target.length === 0 || this.length === 0) return 0 // Fatal error conditions if (targetStart < 0) { throw new RangeError('targetStart out of bounds') } if (start < 0 || start >= this.length) throw new RangeError('sourceStart out of bounds') if (end < 0) throw new RangeError('sourceEnd out of bounds') // Are we oob? if (end > this.length) end = this.length if (target.length - targetStart < end - start) { end = target.length - targetStart + start } var len = end - start var i if (this === target && start < targetStart && targetStart < end) { // descending copy from end for (i = len - 1; i >= 0; i--) { target[i + targetStart] = this[i + start] } } else if (len < 1000 || !Buffer.TYPED_ARRAY_SUPPORT) { // ascending copy from start for (i = 0; i < len; i++) { target[i + targetStart] = this[i + start] } } else { Uint8Array.prototype.set.call( target, this.subarray(start, start + len), targetStart ) } return len } // fill(value, start=0, end=buffer.length) Buffer.prototype.fill = function fill (value, start, end) { if (!value) value = 0 if (!start) start = 0 if (!end) end = this.length if (end < start) throw new RangeError('end < start') // Fill 0 bytes; we're done if (end === start) return if (this.length === 0) return if (start < 0 || start >= this.length) throw new RangeError('start out of bounds') if (end < 0 || end > this.length) throw new RangeError('end out of bounds') var i if (typeof value === 'number') { for (i = start; i < end; i++) { this[i] = value } } else { var bytes = utf8ToBytes(value.toString()) var len = bytes.length for (i = start; i < end; i++) { this[i] = bytes[i % len] } } return this } // HELPER FUNCTIONS // ================ var INVALID_BASE64_RE = /[^+\/0-9A-Za-z-_]/g function base64clean (str) { // Node strips out invalid characters like \n and \t from the string, base64-js does not str = stringtrim(str).replace(INVALID_BASE64_RE, '') // Node converts strings with length < 2 to '' if (str.length < 2) return '' // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not while (str.length % 4 !== 0) { str = str + '=' } return str } function stringtrim (str) { if (str.trim) return str.trim() return str.replace(/^\s+|\s+$/g, '') } function toHex (n) { if (n < 16) return '0' + n.toString(16) return n.toString(16) } function utf8ToBytes (string, units) { units = units || Infinity var codePoint var length = string.length var leadSurrogate = null var bytes = [] for (var i = 0; i < length; i++) { codePoint = string.charCodeAt(i) // is surrogate component if (codePoint > 0xD7FF && codePoint < 0xE000) { // last char was a lead if (!leadSurrogate) { // no lead yet if (codePoint > 0xDBFF) { // unexpected trail if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) continue } else if (i + 1 === length) { // unpaired lead if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) continue } // valid lead leadSurrogate = codePoint continue } // 2 leads in a row if (codePoint < 0xDC00) { if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) leadSurrogate = codePoint continue } // valid surrogate pair codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000 } else if (leadSurrogate) { // valid bmp char, but last char was a lead if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD) } leadSurrogate = null // encode utf8 if (codePoint < 0x80) { if ((units -= 1) < 0) break bytes.push(codePoint) } else if (codePoint < 0x800) { if ((units -= 2) < 0) break bytes.push( codePoint >> 0x6 | 0xC0, codePoint & 0x3F | 0x80 ) } else if (codePoint < 0x10000) { if ((units -= 3) < 0) break bytes.push( codePoint >> 0xC | 0xE0, codePoint >> 0x6 & 0x3F | 0x80, codePoint & 0x3F | 0x80 ) } else if (codePoint < 0x110000) { if ((units -= 4) < 0) break bytes.push( codePoint >> 0x12 | 0xF0, codePoint >> 0xC & 0x3F | 0x80, codePoint >> 0x6 & 0x3F | 0x80, codePoint & 0x3F | 0x80 ) } else { throw new Error('Invalid code point') } } return bytes } function asciiToBytes (str) { var byteArray = [] for (var i = 0; i < str.length; i++) { // Node's code seems to be doing this and not & 0x7F.. byteArray.push(str.charCodeAt(i) & 0xFF) } return byteArray } function utf16leToBytes (str, units) { var c, hi, lo var byteArray = [] for (var i = 0; i < str.length; i++) { if ((units -= 2) < 0) break c = str.charCodeAt(i) hi = c >> 8 lo = c % 256 byteArray.push(lo) byteArray.push(hi) } return byteArray } function base64ToBytes (str) { return base64.toByteArray(base64clean(str)) } function blitBuffer (src, dst, offset, length) { for (var i = 0; i < length; i++) { if ((i + offset >= dst.length) || (i >= src.length)) break dst[i + offset] = src[i] } return i } ================================================ FILE: test/autotests/inspect/complex/expected.json ================================================ { "requires": [ { "path": "lasso-loader", "range": [ 19, 42 ], "argRange": [ 27, 41 ] }, { "path": "foo", "range": [ 151, 165 ], "argRange": [ 159, 164 ] }, { "path": "bar", "range": [ 188, 202 ], "argRange": [ 196, 201 ] }, { "path": "lasso-loader", "range": [ 473, 496 ], "argRange": [ 481, 495 ] } ], "foundGlobals": {}, "asyncBlocks": [ { "requires": [ { "path": "baz", "range": [ 272, 286 ], "argRange": [ 280, 285 ] } ], "dependencies": [ "./browser.json", { "type": "require", "path": "baz" } ], "firstArgRange": [ 228, 246 ], "hasInlineDependencies": true, "hasFunctionBody": true }, { "requires": [ { "path": "cat", "range": [ 355, 369 ], "argRange": [ 363, 368 ] } ], "dependencies": [ { "type": "require", "path": "cat" } ], "firstArgRange": [ 327, 380 ], "hasInlineDependencies": false, "hasFunctionBody": true }, { "requires": [], "dependencies": [ "require: avatar", "require: address" ], "firstArgRange": [ 520, 617 ], "hasInlineDependencies": true, "hasFunctionBody": false } ] } ================================================ FILE: test/autotests/inspect/complex/input.js ================================================ var raptorLoader = require('lasso-loader'); exports.test = function(input) { for (var i=0; i= this.getMaxTokensPerLine() - 1) { tags.push(line.length - position); while (ruleStack.length > initialRuleStackLength) { _ref1 = ruleStack.pop(), scopeName = _ref1.scopeName, contentScopeName = _ref1.contentScopeName; if (contentScopeName) { tags.push(this.endIdForScope(contentScopeName)); } if (scopeName) { tags.push(this.endIdForScope(scopeName)); } } break; } if (match = _.last(ruleStack).rule.getNextTags(ruleStack, line, position, firstLine)) { nextTags = match.nextTags, tagsStart = match.tagsStart, tagsEnd = match.tagsEnd; if (position < tagsStart) { tags.push(tagsStart - position); tokenCount++; } tags.push.apply(tags, nextTags); for (_j = 0, _len1 = nextTags.length; _j < _len1; _j++) { tag = nextTags[_j]; if (tag >= 0) { tokenCount++; } } position = tagsEnd; } else { if (position < line.length || line.length === 0) { tags.push(line.length - position); } break; } if (position === previousPosition) { if (ruleStack.length === previousRuleStackLength) { console.error("Popping rule because it loops at column " + position + " of line '" + line + "'", _.clone(ruleStack)); if (ruleStack.length > 1) { _ref2 = ruleStack.pop(), scopeName = _ref2.scopeName, contentScopeName = _ref2.contentScopeName; if (contentScopeName) { tags.push(this.endIdForScope(contentScopeName)); } if (scopeName) { tags.push(this.endIdForScope(scopeName)); } } else { if (position < line.length || (line.length === 0 && tags.length === 0)) { tags.push(line.length - position); } break; } } else if (ruleStack.length > previousRuleStackLength) { _ref3 = ruleStack.slice(-2), (_ref4 = _ref3[0], penultimateRule = _ref4.rule), (_ref5 = _ref3[1], lastRule = _ref5.rule); if ((lastRule != null) && lastRule === penultimateRule) { popStack = true; } if (((lastRule != null ? lastRule.scopeName : void 0) != null) && penultimateRule.scopeName === lastRule.scopeName) { popStack = true; } if (popStack) { ruleStack.pop(); lastSymbol = _.last(tags); if (lastSymbol < 0 && lastSymbol === this.startIdForScope(lastRule.scopeName)) { tags.pop(); } tags.push(line.length - position); break; } } } } for (_k = 0, _len2 = ruleStack.length; _k < _len2; _k++) { rule = ruleStack[_k].rule; rule.clearAnchorPosition(); } if (compatibilityMode) { return new TokenizeLineResult(line, openScopeTags, tags, ruleStack, this.registry); } else { return { line: line, tags: tags, ruleStack: ruleStack }; } }; Grammar.prototype.activate = function() { return this.registration = this.registry.addGrammar(this); }; Grammar.prototype.deactivate = function() { var _ref; this.emitter = new Emitter; if ((_ref = this.registration) != null) { _ref.dispose(); } return this.registration = null; }; Grammar.prototype.clearRules = function() { this.initialRule = null; return this.repository = null; }; Grammar.prototype.getInitialRule = function() { return this.initialRule != null ? this.initialRule : this.initialRule = this.createRule({ scopeName: this.scopeName, patterns: this.rawPatterns }); }; Grammar.prototype.getRepository = function() { return this.repository != null ? this.repository : this.repository = (function(_this) { return function() { var data, name, repository, _ref; repository = {}; _ref = _this.rawRepository; for (name in _ref) { data = _ref[name]; if ((data.begin != null) || (data.match != null)) { data = { patterns: [data], tempName: name }; } repository[name] = _this.createRule(data); } return repository; }; })(this)(); }; Grammar.prototype.addIncludedGrammarScope = function(scope) { if (!_.include(this.includedGrammarScopes, scope)) { return this.includedGrammarScopes.push(scope); } }; Grammar.prototype.grammarUpdated = function(scopeName) { if (!_.include(this.includedGrammarScopes, scopeName)) { return false; } this.clearRules(); this.registry.grammarUpdated(this.scopeName); if (Grim.includeDeprecatedAPIs) { this.emit('grammar-updated'); } this.emitter.emit('did-update'); return true; }; Grammar.prototype.startIdForScope = function(scope) { return this.registry.startIdForScope(scope); }; Grammar.prototype.endIdForScope = function(scope) { return this.registry.endIdForScope(scope); }; Grammar.prototype.scopeForId = function(id) { return this.registry.scopeForId(id); }; Grammar.prototype.createRule = function(options) { return new Rule(this, this.registry, options); }; Grammar.prototype.createPattern = function(options) { return new Pattern(this, this.registry, options); }; Grammar.prototype.getMaxTokensPerLine = function() { return this.maxTokensPerLine; }; Grammar.prototype.scopesFromStack = function(stack, rule, endPatternMatch) { var contentScopeName, scopeName, scopes, _i, _len, _ref; scopes = []; for (_i = 0, _len = stack.length; _i < _len; _i++) { _ref = stack[_i], scopeName = _ref.scopeName, contentScopeName = _ref.contentScopeName; if (scopeName) { scopes.push(scopeName); } if (contentScopeName) { scopes.push(contentScopeName); } } if (endPatternMatch && (rule != null ? rule.contentScopeName : void 0) && rule === stack[stack.length - 1]) { scopes.pop(); } return scopes; }; return Grammar; })(); if (Grim.includeDeprecatedAPIs) { EmitterMixin = require('emissary').Emitter; EmitterMixin.includeInto(Grammar); Grammar.prototype.on = function(eventName) { if (eventName === 'did-update') { Grim.deprecate("Call Grammar::onDidUpdate instead"); } else { Grim.deprecate("Call explicit event subscription methods instead"); } return EmitterMixin.prototype.on.apply(this, arguments); }; } TokenizeLineResult = (function() { function TokenizeLineResult(line, openScopeTags, tags, ruleStack, registry) { this.line = line; this.openScopeTags = openScopeTags; this.tags = tags; this.ruleStack = ruleStack; this.registry = registry; } Object.defineProperty(TokenizeLineResult.prototype, 'tokens', { get: function() { return this.registry.decodeTokens(this.line, this.tags, this.openScopeTags); } }); return TokenizeLineResult; })(); }).call(this); ================================================ FILE: test/autotests/inspect/global-late-def/expected.json ================================================ { "requires": [], "foundGlobals": {}, "asyncBlocks": [] } ================================================ FILE: test/autotests/inspect/global-late-def/input.js ================================================ exports.Foo = Foo; function Foo() { } ================================================ FILE: test/autotests/inspect/globals/expected.json ================================================ { "requires": [], "foundGlobals": { "propertyValue": true, "async": true, "process": true, "arrayEl": true, "callArg": true }, "asyncBlocks": [] } ================================================ FILE: test/autotests/inspect/globals/input.js ================================================ var foo = { bar: propertyValue }; async.series(process.nextTick); foo = [ arrayEl ]; var a = function() { foo(callArg); } ================================================ FILE: test/autotests/inspect/lasso-loader-var/expected.json ================================================ { "requires": [ { "path": "lasso-loader", "range": [ 19, 42 ], "argRange": [ 27, 41 ] } ], "foundGlobals": {}, "asyncBlocks": [ { "requires": [ { "path": "baz", "range": [ 146, 160 ], "argRange": [ 154, 159 ] } ], "dependencies": [ "./browser.json", { "type": "require", "path": "baz" } ], "firstArgRange": [ 102, 120 ], "hasInlineDependencies": true, "hasFunctionBody": true }, { "requires": [ { "path": "cat", "range": [ 218, 232 ], "argRange": [ 226, 231 ] } ], "dependencies": [ { "type": "require", "path": "cat" } ], "firstArgRange": [ 190, 243 ], "hasInlineDependencies": false, "hasFunctionBody": true } ] } ================================================ FILE: test/autotests/inspect/lasso-loader-var/input.js ================================================ var raptorLoader = require('lasso-loader'); exports.test = function(input) { raptorLoader.async(['./browser.json'], function(err) { require('baz'); raptorLoader.async(function(err) { require('cat'); }); }); }; ================================================ FILE: test/autotests/inspect/parse-error/expected.json ================================================ { "name": "SyntaxError", "message": "Unexpected token ;\n> 1 | const thing = {;\n | ^\n 2 | require('foo');\n 3 |\n 4 | if (require('bar')) {" } ================================================ FILE: test/autotests/inspect/parse-error/input.js ================================================ const thing = {; require('foo'); if (require('bar')) { require('baz'); } ================================================ FILE: test/autotests/inspect/process1/expected.json ================================================ { "requires": [], "foundGlobals": { "process": true }, "asyncBlocks": [] } ================================================ FILE: test/autotests/inspect/process1/input.js ================================================ var p = process; p.nextTick(); ================================================ FILE: test/autotests/inspect/process2/expected.json ================================================ { "requires": [], "foundGlobals": { "process": true }, "asyncBlocks": [] } ================================================ FILE: test/autotests/inspect/process2/input.js ================================================ exports.hello = function() { process.nextTick(function() { }); } ================================================ FILE: test/autotests/inspect/process3/expected.json ================================================ { "requires": [], "foundGlobals": {}, "asyncBlocks": [] } ================================================ FILE: test/autotests/inspect/process3/input.js ================================================ var process = {}; process.nextTick(); ================================================ FILE: test/autotests/inspect/process4/expected.json ================================================ { "requires": [], "foundGlobals": { "async": true, "process": true }, "asyncBlocks": [] } ================================================ FILE: test/autotests/inspect/process4/input.js ================================================ function test() { async.parallel(process.nextTick); } ================================================ FILE: test/autotests/inspect/simple/expected.json ================================================ { "requires": [ { "path": "foo", "range": [ 0, 14 ], "argRange": [ 8, 13 ] }, { "path": "bar", "range": [ 21, 35 ], "argRange": [ 29, 34 ] }, { "path": "baz", "range": [ 43, 57 ], "argRange": [ 51, 56 ] } ], "foundGlobals": {}, "asyncBlocks": [] } ================================================ FILE: test/autotests/inspect/simple/input.js ================================================ require('foo'); if (require('bar')) { require('baz'); } ================================================ FILE: test/autotests/load-prebuild/error-invalid-path/test.js ================================================ const path = require('path'); const expect = require('chai').expect; const prebuildPath = path.join(__dirname, './page.prebuild.json'); exports.check = async function (lasso) { const lassoPageResult = await lasso.loadPrebuild({ path: prebuildPath, flags: ['mobile'] }); expect(lassoPageResult.getHtmlForSlot('body')).to.equal(''); expect(lassoPageResult.getHtmlForSlot('head')).to.equal(''); }; exports.checkError = function (err) { expect(err.message).to.contain(`Error loading prebuild. No prebuild with path "${prebuildPath}" exists.`); }; ================================================ FILE: test/autotests/load-prebuild/error-no-build/page.prebuild.json ================================================ [ { "slots": { "body": [{ "contentType": "js", "content": [{ "code": { "src": "./test.js" } }] }], "head": [{ "contentType": "css", "content": [{ "code": { "href": "./style.css" }}] }] }, "assets": [], "name": "page", "flags": ["mobile"] } ] ================================================ FILE: test/autotests/load-prebuild/error-no-build/test.js ================================================ const path = require('path'); const expect = require('chai').expect; const prebuildPath = path.join(__dirname, './page.prebuild.json'); exports.check = async function (lasso) { const lassoPageResult = await lasso.loadPrebuild({ path: prebuildPath }); expect(lassoPageResult.getHtmlForSlot('body')).to.equal(''); expect(lassoPageResult.getHtmlForSlot('head')).to.equal(''); }; exports.checkError = function (err) { expect(err.message).to.contain(`No build could be found using flags: "undefined" for file at path "${prebuildPath}"`); }; ================================================ FILE: test/autotests/load-prebuild/load-valid-prebuild/page.prebuild.json ================================================ [ { "slots": { "body": [{ "contentType": "js", "content": [{ "code": { "src": "./test.js" } }] }], "head": [{ "contentType": "css", "content": [{ "code": { "href": "./style.css" }}] }] }, "assets": [], "name": "page" } ] ================================================ FILE: test/autotests/load-prebuild/load-valid-prebuild/test.js ================================================ const path = require('path'); const expect = require('chai').expect; exports.check = async function (lasso) { const prebuildPath = path.join(__dirname, './page.prebuild.json'); const lassoPageResult = await lasso.loadPrebuild({ path: prebuildPath }); expect(lassoPageResult.getHtmlForSlot('body')).to.equal(''); expect(lassoPageResult.getHtmlForSlot('head')).to.equal(''); }; ================================================ FILE: test/autotests/load-prebuild/load-valid-prebuild-multi-flags/page.prebuild.json ================================================ [ { "slots": { "body": [{ "contentType": "js", "content": [{ "code": { "src": "./test.js" } }] }], "head": [{ "contentType": "css", "content": [{ "code": { "href": "./style.css" }}] }] }, "assets": [], "name": "page" }, { "slots": { "body": [{ "contentType": "js", "content": [{ "code": { "src": "./test1.js" } }] }], "head": [{ "contentType": "css", "content": [{ "code": { "href": "./style.css" }}] }] }, "flags": ["mobile", "test"], "assets": [], "name": "page" }, { "slots": { "body": [{ "contentType": "js", "content": [{ "code": { "src": "./test2.js" } }] }], "head": [{ "contentType": "css", "content": [{ "code": { "href": "./style.css" }}] }] }, "flags": [], "assets": [], "name": "page" } ] ================================================ FILE: test/autotests/load-prebuild/load-valid-prebuild-multi-flags/test.js ================================================ const path = require('path'); const expect = require('chai').expect; exports.check = async function (lasso) { const prebuildPath = path.join(__dirname, './page.prebuild.json'); const lassoPageResult = await lasso.loadPrebuild({ path: prebuildPath, flags: ['mobile', 'test'] }); expect(lassoPageResult.getHtmlForSlot('body')).to.equal(''); expect(lassoPageResult.getHtmlForSlot('head')).to.equal(''); }; ================================================ FILE: test/autotests/load-prebuild/load-valid-prebuild-multi-no-flags/page.prebuild.json ================================================ [ { "slots": { "body": [{ "contentType": "js", "content": [{ "code": { "src": "./test.js" } }] }], "head": [{ "contentType": "css", "content": [{ "code": { "href": "./style.css" }}] }] }, "assets": [], "name": "page" }, { "slots": { "body": [{ "contentType": "js", "content": [{ "code": { "src": "./test1.js" } }] }], "head": [{ "contentType": "css", "content": [{ "code": { "href": "./style.css" }}] }] }, "flags": ["mobile", "test"], "assets": [], "name": "page" }, { "slots": { "body": [{ "contentType": "js", "content": [{ "code": { "src": "./test2.js" } }] }], "head": [{ "contentType": "css", "content": [{ "code": { "href": "./style.css" }}] }] }, "flags": [], "assets": [], "name": "page" } ] ================================================ FILE: test/autotests/load-prebuild/load-valid-prebuild-multi-no-flags/test.js ================================================ const path = require('path'); const expect = require('chai').expect; exports.check = async function (lasso) { const prebuildPath = path.join(__dirname, './page.prebuild.json'); const lassoPageResult = await lasso.loadPrebuild({ path: prebuildPath }); expect(lassoPageResult.getHtmlForSlot('body')).to.equal(''); expect(lassoPageResult.getHtmlForSlot('head')).to.equal(''); }; ================================================ FILE: test/autotests/modules/async/foo.js ================================================ window.fooLoaded = true; exports.filename = __filename; exports.isFoo = true; ================================================ FILE: test/autotests/modules/async/lasso-loader-patch.js ================================================ var lassoLoader = require('lasso-loader'); var path = require('path'); var loaderMeta = module.__loaderMetadata; function _handleMissingAsync(asyncId) { if (asyncId.charAt(0) === '_') { return; } else { throw new Error('No loader metadata for ' + asyncId); } } lassoLoader.async = function(asyncId, callback) { if (!loaderMeta) { return callback(); } var resources; if (Array.isArray(asyncId)) { resources = { js: [], css: [] }; asyncId.forEach(function(asyncId) { var curResources = loaderMeta[asyncId]; if (curResources) { ['js', 'css'].forEach(function(key) { var paths = curResources[key]; if (paths) { resources[key] = resources[key].concat(paths); } }); } else { _handleMissingAsync(asyncId); } }); } else if (!(resources = loaderMeta[asyncId])) { _handleMissingAsync(asyncId); return callback(); } var job; var modulesRuntime = require.runtime; if (modulesRuntime) { // Create a pending job in the module runtime system which will // prevent any "require-run" modules from running if they are // configured to wait until ready. // When all pending jobs are completed, the "require-run" modules // that have been queued up will be ran. job = modulesRuntime.pending(); } var jsUrls = resources.js; if (jsUrls) { jsUrls.forEach((url) => { var filename = path.join($outputDir, url); $loadScript(filename); }); } return callback(); }; ================================================ FILE: test/autotests/modules/async/main.js ================================================ exports.filename = __filename; require('./lasso-loader-patch'); exports.loadFoo = function(callback) { require('lasso-loader').async(function(err) { if (err) { return callback(err); } var foo = require('./foo'); callback(null, foo); }); }; window.main = module.exports; ================================================ FILE: test/autotests/modules/async/package.json ================================================ { "name": "autotest", "version": "1.0.0" } ================================================ FILE: test/autotests/modules/async/test.js ================================================ var expect = require('chai').expect; exports.getLassoOptions = function(dir) { return { dependencies: [ 'require-run: ./main' ] }; }; exports.check = function (window) { expect(window.fooLoaded).to.equal(undefined); expect(window.main.filename).to.contain('main'); return new Promise((resolve, reject) => { window.main.loadFoo(function(err) { if (err) { return reject(err); } resolve(); }); }); }; ================================================ FILE: test/autotests/modules/async-flags/foo.js ================================================ window.fooLoaded = true; exports.filename = __filename; exports.isFoo = true; ================================================ FILE: test/autotests/modules/async-flags/lasso-loader-patch.js ================================================ var lassoLoader = require('lasso-loader'); var path = require('path'); var loaderMeta = module.__loaderMetadata; function _handleMissingAsync(asyncId) { if (asyncId.charAt(0) === '_') { return; } else { throw new Error('No loader metadata for ' + asyncId); } } lassoLoader.async = function(asyncId, callback) { if (!loaderMeta) { return callback(); } var resources; if (Array.isArray(asyncId)) { resources = { js: [], css: [] }; asyncId.forEach(function(asyncId) { var curResources = loaderMeta[asyncId]; if (curResources) { ['js', 'css'].forEach(function(key) { var paths = curResources[key]; if (paths) { resources[key] = resources[key].concat(paths); } }); } else { _handleMissingAsync(asyncId); } }); } else if (!(resources = loaderMeta[asyncId])) { _handleMissingAsync(asyncId); return callback(); } var job; var modulesRuntime = require.runtime; if (modulesRuntime) { // Create a pending job in the module runtime system which will // prevent any "require-run" modules from running if they are // configured to wait until ready. // When all pending jobs are completed, the "require-run" modules // that have been queued up will be ran. job = modulesRuntime.pending(); } var jsUrls = resources.js; if (jsUrls) { jsUrls.forEach((url) => { var filename = path.join($outputDir, url); $loadScript(filename); }); } return callback(); }; ================================================ FILE: test/autotests/modules/async-flags/main.js ================================================ exports.filename = __filename; require('./lasso-loader-patch'); exports.loadFoo = function(callback) { require('lasso-loader').async(function(err) { if (err) { return callback(err); } var foo = require('./foo'); callback(null, foo); }); }; window.main = module.exports; ================================================ FILE: test/autotests/modules/async-flags/package.json ================================================ { "name": "autotest", "version": "1.0.0" } ================================================ FILE: test/autotests/modules/async-flags/test.js ================================================ var expect = require('chai').expect; exports.getLassoOptions = function(dir) { return { dependencies: [ 'require-run: ./main' ] }; }; exports.check = function (window) { expect(window.fooLoaded).to.equal(undefined); expect(window.main.filename).to.contain('main'); return new Promise((resolve, reject) => { window.main.loadFoo(function(err) { if (err) { return reject(err); } resolve(); }); }); }; ================================================ FILE: test/autotests/modules/async-intersection/foo.js ================================================ window.fooLoaded = true; exports.filename = __filename; exports.isFoo = true; ================================================ FILE: test/autotests/modules/async-intersection/lasso-loader-patch.js ================================================ var lassoLoader = require('lasso-loader'); var path = require('path'); var loaderMeta = module.__loaderMetadata; function _handleMissingAsync(asyncId) { if (asyncId.charAt(0) === '_') { return; } else { throw new Error('No loader metadata for ' + asyncId); } } lassoLoader.async = function(asyncId, callback) { if (!loaderMeta) { return callback(); } var resources; if (Array.isArray(asyncId)) { resources = { js: [], css: [] }; asyncId.forEach(function(asyncId) { var curResources = loaderMeta[asyncId]; if (curResources) { ['js', 'css'].forEach(function(key) { var paths = curResources[key]; if (paths) { resources[key] = resources[key].concat(paths); } }); } else { _handleMissingAsync(asyncId); } }); } else if (!(resources = loaderMeta[asyncId])) { _handleMissingAsync(asyncId); return callback(); } var job; var modulesRuntime = require.runtime; if (modulesRuntime) { // Create a pending job in the module runtime system which will // prevent any "require-run" modules from running if they are // configured to wait until ready. // When all pending jobs are completed, the "require-run" modules // that have been queued up will be ran. job = modulesRuntime.pending(); } var jsUrls = resources.js; if (jsUrls) { jsUrls.forEach((url) => { var filename = path.join($outputDir, url); $loadScript(filename); }); } return callback(); }; ================================================ FILE: test/autotests/modules/async-intersection/main1-helper.js ================================================ exports.isMain1Helper = true; ================================================ FILE: test/autotests/modules/async-intersection/main1.js ================================================ exports.filename = __filename; require('./lasso-loader-patch'); exports.load = function(callback) { require('lasso-loader').async(function(err) { if (err) { return callback(err); } var foo = require('./foo'); callback(null, { foo: foo, helper: require('./main1-helper') }); }); }; window.main = module.exports; ================================================ FILE: test/autotests/modules/async-intersection/main2-helper.js ================================================ exports.isMain2Helper = true; ================================================ FILE: test/autotests/modules/async-intersection/main2.js ================================================ exports.filename = __filename; require('./lasso-loader-patch'); exports.load = function(callback) { require('lasso-loader').async(function(err) { if (err) { return callback(err); } var foo = require('./foo'); callback(null, { foo: foo, helper: require('./main2-helper') }); }); }; window.main = module.exports; ================================================ FILE: test/autotests/modules/async-intersection/package.json ================================================ { "name": "autotest", "version": "1.0.0" } ================================================ FILE: test/autotests/modules/async-intersection/test.js ================================================ const expect = require('chai').expect; exports.lassoConfig = { bundlingEnabled: true, fingerprintsEnabled: false, bundles: [ { name: 'common', dependencies: [ { "intersection": [ 'require-run: ' + require.resolve('./main1'), 'require-run: ' + require.resolve('./main2') ] } ] } ] }; exports.tests = [ { lassoOptions: { pageName: 'main1', dependencies: [ 'require-run: ' + require.resolve('./main1') ] }, check (window) { expect(window.fooLoaded).to.equal(undefined); expect(window.main.filename).to.contain('main'); return new Promise((resolve, reject) => { window.main.load(function(err, loaded) { if (err) { return reject(err); } try { expect(window.fooLoaded).to.equal(true); expect(loaded.foo.isFoo).to.equal(true); expect(loaded.helper.isMain1Helper).to.equal(true); } catch (err) { return reject(err); } resolve(); }); }); } }, { lassoOptions: { pageName: 'main2', dependencies: [ 'require-run: ' + require.resolve('./main2') ] }, check (window) { expect(window.fooLoaded).to.equal(undefined); expect(window.main.filename).to.contain('main'); return new Promise((resolve, reject) => { window.main.load(function(err, loaded) { if (err) { return reject(err); } try { expect(window.fooLoaded).to.equal(true); expect(loaded.foo.isFoo).to.equal(true); expect(loaded.helper.isMain2Helper).to.equal(true); } catch (err) { return reject(err); } resolve(); }); }); } } ]; ================================================ FILE: test/autotests/modules/async-package/bar.js ================================================ window.barLoaded = true; exports.filename = __filename; exports.isBar = true; ================================================ FILE: test/autotests/modules/async-package/browser.json ================================================ { "async": { "foo": [ "require: ./foo" ], "bar": [ "require: ./bar" ], "something": [ "./something.js" ] } } ================================================ FILE: test/autotests/modules/async-package/foo.js ================================================ window.fooLoaded = true; exports.filename = __filename; exports.isFoo = true; ================================================ FILE: test/autotests/modules/async-package/lasso-loader-patch.js ================================================ var lassoLoader = require('lasso-loader'); var path = require('path'); var loaderMeta = module.__loaderMetadata; function _handleMissingAsync(asyncId) { if (asyncId.charAt(0) === '_') { return; } else { throw new Error('No loader metadata for ' + asyncId); } } lassoLoader.async = function(asyncId, callback) { if (!loaderMeta) { return callback(); } var resources; if (Array.isArray(asyncId)) { resources = { js: [], css: [] }; asyncId.forEach(function(asyncId) { var curResources = loaderMeta[asyncId]; if (curResources) { ['js', 'css'].forEach(function(key) { var paths = curResources[key]; if (paths) { resources[key] = resources[key].concat(paths); } }); } else { _handleMissingAsync(asyncId); } }); } else if (!(resources = loaderMeta[asyncId])) { _handleMissingAsync(asyncId); return callback(); } var job; var modulesRuntime = require.runtime; if (modulesRuntime) { // Create a pending job in the module runtime system which will // prevent any "require-run" modules from running if they are // configured to wait until ready. // When all pending jobs are completed, the "require-run" modules // that have been queued up will be ran. job = modulesRuntime.pending(); } var jsUrls = resources.js; if (jsUrls) { jsUrls.forEach((url) => { var filename = path.join($outputDir, url); $loadScript(filename); }); } return callback(); }; ================================================ FILE: test/autotests/modules/async-package/main.js ================================================ exports.filename = __filename; require('./lasso-loader-patch'); exports.loadFoo = function(callback) { require('lasso-loader').async('foo', function(err) { if (err) { return callback(err); } var foo = require('./foo'); callback(null, foo); }); }; var barPackageId = 'bar'; exports.loadBar = function(callback) { require('lasso-loader').async(barPackageId + '', function(err) { if (err) { return callback(err); } var bar = require('./bar'); callback(null, bar); }); }; exports.loadSomething = function(callback) { require('lasso-loader').async('something', callback); }; window.main = module.exports; ================================================ FILE: test/autotests/modules/async-package/package.json ================================================ { "name": "autotest", "version": "1.0.0" } ================================================ FILE: test/autotests/modules/async-package/something.js ================================================ window.somethingLoaded = true; ================================================ FILE: test/autotests/modules/async-package/test.js ================================================ var expect = require('chai').expect; exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundles: [ { name: 'main', dependencies: [ 'require-run: ./main' ] }, { name: 'foo', dependencies: [ 'require: ./foo' ] }, { name: 'something', dependencies: [ './something.js' ] } ] }; }; exports.getLassoOptions = function(dir) { return { dependencies: [ 'require-run: ./main' ] }; }; exports.check = async function (window) { expect(window.fooLoaded).to.equal(undefined); expect(window.main.filename).to.contain('main'); return new Promise((resolve, reject) => { window.main.loadFoo(function(err, foo) { if (err) { return reject(err); } expect(foo.isFoo).to.equal(true); expect(window.fooLoaded).to.equal(true); expect(window.barLoaded).to.equal(undefined); expect(window.somethingLoaded).to.equal(undefined); window.main.loadBar(function(err, bar) { if (err) { return reject(err); } expect(bar.isBar).to.equal(true); expect(window.fooLoaded).to.equal(true); expect(window.barLoaded).to.equal(true); expect(window.somethingLoaded).to.equal(undefined); window.main.loadSomething(function(err) { if (err) { return reject(err); } expect(window.fooLoaded).to.equal(true); expect(window.barLoaded).to.equal(true); expect(window.somethingLoaded).to.equal(true); resolve(); }); }); }); }); }; ================================================ FILE: test/autotests/modules/async-package-css/browser.json ================================================ { "async": { "something": [ "./something.css" ] } } ================================================ FILE: test/autotests/modules/async-package-css/lasso-loader-patch.js ================================================ var lassoLoader = require('lasso-loader'); var path = require('path'); var loaderMeta = module.__loaderMetadata; function _handleMissingAsync(asyncId) { if (asyncId.charAt(0) === '_') { return; } else { throw new Error('No loader metadata for ' + asyncId); } } lassoLoader.async = function(asyncId, callback) { if (!loaderMeta) { return callback(); } var resources; if (Array.isArray(asyncId)) { resources = { js: [], css: [] }; asyncId.forEach(function(asyncId) { var curResources = loaderMeta[asyncId]; if (curResources) { ['js', 'css'].forEach(function(key) { var paths = curResources[key]; if (paths) { resources[key] = resources[key].concat(paths); } }); } else { _handleMissingAsync(asyncId); } }); } else if (!(resources = loaderMeta[asyncId])) { _handleMissingAsync(asyncId); return callback(); } var job; var modulesRuntime = require.runtime; if (modulesRuntime) { // Create a pending job in the module runtime system which will // prevent any "require-run" modules from running if they are // configured to wait until ready. // When all pending jobs are completed, the "require-run" modules // that have been queued up will be ran. job = modulesRuntime.pending(); } var jsUrls = resources.js; if (jsUrls) { jsUrls.forEach((url) => { var filename = path.join($outputDir, url); $loadScript(filename); }); } return callback(); }; ================================================ FILE: test/autotests/modules/async-package-css/main.js ================================================ exports.filename = __filename; require('./lasso-loader-patch'); exports.loadSomething = function(callback) { require('lasso-loader').async('something', callback); }; window.main = module.exports; ================================================ FILE: test/autotests/modules/async-package-css/package.json ================================================ { "name": "autotest", "version": "1.0.0" } ================================================ FILE: test/autotests/modules/async-package-css/something.css ================================================ .test {} ================================================ FILE: test/autotests/modules/async-package-css/test.js ================================================ const expect = require('chai').expect; const path = require('path'); exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundles: [ { name: 'main', dependencies: [ 'require-run: ./main' ] }, { name: 'something', dependencies: [ './something.css' ] } ] }; }; exports.getLassoOptions = function(dir) { return { dependencies: [ 'require-run: ./main' ] }; }; exports.check = async function (window, lassoPageResult) { expect(window.fooLoaded).to.equal(undefined); expect(window.main.filename).to.contain('main'); return new Promise((resolve, reject) => { window.main.loadSomething(function(err) { if (err) { return reject(err); } expect(lassoPageResult.getCSSUrls()) .to.deep.equal(['./something.css']); expect(lassoPageResult.getUrlByAsyncBundleName('something')) .to.equal('./something.css'); expect(lassoPageResult.getFileByAsyncBundleName('something').endsWith('/something.css')) .to.equal(true); resolve(); }); }); }; ================================================ FILE: test/autotests/modules/async-raptor-loader/foo.js ================================================ window.fooLoaded = true; exports.filename = __filename; exports.isFoo = true; ================================================ FILE: test/autotests/modules/async-raptor-loader/lasso-loader-patch.js ================================================ var lassoLoader = require('lasso-loader'); var path = require('path'); var loaderMeta = module.__loaderMetadata; function _handleMissingAsync(asyncId) { if (asyncId.charAt(0) === '_') { return; } else { throw new Error('No loader metadata for ' + asyncId); } } lassoLoader.async = function(asyncId, callback) { if (!loaderMeta) { return callback(); } var resources; if (Array.isArray(asyncId)) { resources = { js: [], css: [] }; asyncId.forEach(function(asyncId) { var curResources = loaderMeta[asyncId]; if (curResources) { ['js', 'css'].forEach(function(key) { var paths = curResources[key]; if (paths) { resources[key] = resources[key].concat(paths); } }); } else { _handleMissingAsync(asyncId); } }); } else if (!(resources = loaderMeta[asyncId])) { _handleMissingAsync(asyncId); return callback(); } var job; var modulesRuntime = require.runtime; if (modulesRuntime) { // Create a pending job in the module runtime system which will // prevent any "require-run" modules from running if they are // configured to wait until ready. // When all pending jobs are completed, the "require-run" modules // that have been queued up will be ran. job = modulesRuntime.pending(); } var jsUrls = resources.js; if (jsUrls) { jsUrls.forEach((url) => { var filename = path.join($outputDir, url); $loadScript(filename); }); } return callback(); }; ================================================ FILE: test/autotests/modules/async-raptor-loader/main.js ================================================ exports.filename = __filename; require('./lasso-loader-patch'); exports.loadFoo = function(callback) { require('raptor-loader').async(function(err) { if (err) { return callback(err); } var foo = require('./foo'); callback(null, foo); }); }; window.main = module.exports; ================================================ FILE: test/autotests/modules/async-raptor-loader/package.json ================================================ { "name": "autotest", "version": "1.0.0" } ================================================ FILE: test/autotests/modules/async-raptor-loader/test.js ================================================ var expect = require('chai').expect; exports.getLassoOptions = function(dir) { return { dependencies: [ 'require-run: ./main' ] }; }; exports.check = function (window) { expect(window.fooLoaded).to.equal(undefined); expect(window.main.filename).to.contain('main'); return new Promise((resolve, reject) => { window.main.loadFoo(function(err) { if (err) { return reject(err); } resolve(); }); }); }; ================================================ FILE: test/autotests/modules/async-unnecessary/foo.js ================================================ window.fooLoaded = true; exports.filename = __filename; exports.isFoo = true; ================================================ FILE: test/autotests/modules/async-unnecessary/lasso-loader-patch.js ================================================ var lassoLoader = require('lasso-loader'); var path = require('path'); var loaderMeta = module.__loaderMetadata; function _handleMissingAsync(asyncId) { if (asyncId.charAt(0) === '_') { return; } else { throw new Error('No loader metadata for ' + asyncId); } } lassoLoader.async = function(asyncId, callback) { if (!loaderMeta) { return callback(); } var resources; if (Array.isArray(asyncId)) { resources = { js: [], css: [] }; asyncId.forEach(function(asyncId) { var curResources = loaderMeta[asyncId]; if (curResources) { ['js', 'css'].forEach(function(key) { var paths = curResources[key]; if (paths) { resources[key] = resources[key].concat(paths); } }); } else { _handleMissingAsync(asyncId); } }); } else if (!(resources = loaderMeta[asyncId])) { _handleMissingAsync(asyncId); return callback(); } var job; var modulesRuntime = require.runtime; if (modulesRuntime) { // Create a pending job in the module runtime system which will // prevent any "require-run" modules from running if they are // configured to wait until ready. // When all pending jobs are completed, the "require-run" modules // that have been queued up will be ran. job = modulesRuntime.pending(); } var jsUrls = resources.js; if (jsUrls) { jsUrls.forEach((url) => { var filename = path.join($outputDir, url); $loadScript(filename); }); } return callback(); }; ================================================ FILE: test/autotests/modules/async-unnecessary/main.js ================================================ exports.filename = __filename; require('./lasso-loader-patch'); require('./foo'); exports.loadFoo = function(callback) { require('lasso-loader').async(function(err) { if (err) { return callback(err); } var foo = require('./foo'); callback(null, foo); }); }; window.main = module.exports; ================================================ FILE: test/autotests/modules/async-unnecessary/package.json ================================================ { "name": "autotest", "version": "1.0.0" } ================================================ FILE: test/autotests/modules/async-unnecessary/test.js ================================================ var expect = require('chai').expect; exports.getLassoOptions = function(dir) { return { dependencies: [ 'require-run: ./main' ] }; }; exports.check = function (window) { expect(window.fooLoaded).to.equal(true); expect(window.main.filename).to.contain('main'); return new Promise((resolve, reject) => { window.main.loadFoo(function(err, foo) { if (err) { return reject(err); } expect(foo.isFoo).to.equal(true); resolve(); }); }); }; ================================================ FILE: test/autotests/modules/browser.json-tilde/browser.json ================================================ { "dependencies": [ "~/nested/browser.json" ] } ================================================ FILE: test/autotests/modules/browser.json-tilde/nested/browser.json ================================================ { "dependencies": [ "./nested.js" ] } ================================================ FILE: test/autotests/modules/browser.json-tilde/nested/nested.js ================================================ window.nested = true; ================================================ FILE: test/autotests/modules/browser.json-tilde/package.json ================================================ { "name": "autotest", "version": "1.0.0" } ================================================ FILE: test/autotests/modules/browser.json-tilde/test.js ================================================ var expect = require('chai').expect; require('require-self-ref'); exports.getLassoOptions = function(dir) { return { dependencies: [ require.resolve('./browser.json') ] }; }; exports.check = function(window) { expect(window.nested).to.equal(true); }; ================================================ FILE: test/autotests/modules/browserify-transform/foo-transform.js ================================================ 'use strict'; var Transform = require('stream').Transform; class MyTransform extends Transform { constructor(options) { super(options); this.string = ''; } _transform(data, encoding, callback) { this.string += data; callback(); } _flush(callback) { var string = this.string.replace(/foo/g, 'bar'); this.push(string); callback(); } } module.exports = function(file, opts) { return new MyTransform(); }; ================================================ FILE: test/autotests/modules/browserify-transform/foo.js ================================================ module.exports = { filename: __filename, FOO: true }; ================================================ FILE: test/autotests/modules/browserify-transform/main.js ================================================ exports.filename = __filename; exports.foo = 'foo'; window.main = module.exports; ================================================ FILE: test/autotests/modules/browserify-transform/package.json ================================================ { "name": "autotest", "version": "1.0.0" } ================================================ FILE: test/autotests/modules/browserify-transform/test.js ================================================ var expect = require('chai').expect; exports.getLassoConfig = function(dir) { return { bundlingEnabled: false, fingerprintsEnabled: false, require: { transforms: [ require('./foo-transform') ] } }; }; exports.getLassoOptions = function(dir) { return { dependencies: [ 'require-run: ./main' ] }; }; exports.check = function(window) { expect(window.main.filename).to.contain('main'); expect(window.main.bar).to.equal('bar'); }; ================================================ FILE: test/autotests/modules/browserify-transforms/bar-transform.js ================================================ 'use strict'; var Transform = require('stream').Transform; class MyTransform extends Transform { constructor(options) { super(options); this.string = ''; } _transform(data, encoding, callback) { this.string += data; callback(); } _flush(callback) { var string = this.string.replace(/bar/g, 'BAR'); this.push(string); callback(); } } module.exports = function(file, opts) { return new MyTransform(); }; ================================================ FILE: test/autotests/modules/browserify-transforms/foo-transform.js ================================================ 'use strict'; var Transform = require('stream').Transform; class MyTransform extends Transform { constructor(options) { super(options); this.string = ''; } _transform(data, encoding, callback) { this.string += data; callback(); } _flush(callback) { var string = this.string.replace(/foo/g, 'bar'); this.push(string); callback(); } } module.exports = function(file, opts) { return new MyTransform(); }; ================================================ FILE: test/autotests/modules/browserify-transforms/foo.js ================================================ module.exports = { filename: __filename, FOO: true }; ================================================ FILE: test/autotests/modules/browserify-transforms/main.js ================================================ exports.filename = __filename; exports.foo = 'foo'; window.main = module.exports; ================================================ FILE: test/autotests/modules/browserify-transforms/package.json ================================================ { "name": "autotest", "version": "1.0.0" } ================================================ FILE: test/autotests/modules/browserify-transforms/test.js ================================================ var expect = require('chai').expect; exports.getLassoConfig = function(dir) { return { bundlingEnabled: false, fingerprintsEnabled: false, require: { transforms: [ require('./foo-transform'), require('./bar-transform') ] } }; }; exports.getLassoOptions = function(dir) { return { dependencies: [ 'require-run: ./main' ] }; }; exports.check = function(window) { expect(window.main.filename).to.contain('main'); expect(window.main.BAR).to.equal('BAR'); }; ================================================ FILE: test/autotests/modules/duplicate-name-browser-json/bar/browser.json ================================================ { "dependencies": [ "require: ./hello" ] } ================================================ FILE: test/autotests/modules/duplicate-name-browser-json/bar/hello.js ================================================ exports.hello = 'bar'; ================================================ FILE: test/autotests/modules/duplicate-name-browser-json/browser.json ================================================ { "dependencies": [ "./foo/browser.json", "./bar/browser.json", "require-run: ./main" ] } ================================================ FILE: test/autotests/modules/duplicate-name-browser-json/foo/browser.json ================================================ { "dependencies": [ "require: ./hello" ] } ================================================ FILE: test/autotests/modules/duplicate-name-browser-json/foo/hello.js ================================================ exports.hello = 'foo'; ================================================ FILE: test/autotests/modules/duplicate-name-browser-json/main.js ================================================ var fooPath = './foo/hello'; var barPath = './bar/hello'; exports.foo = require(fooPath); exports.bar = require(barPath); window.main = exports; ================================================ FILE: test/autotests/modules/duplicate-name-browser-json/package.json ================================================ { "name": "autotest", "version": "1.0.0" } ================================================ FILE: test/autotests/modules/duplicate-name-browser-json/test.js ================================================ var expect = require('chai').expect; exports.getLassoOptions = function(dir) { return { dependencies: [ require.resolve('./browser.json') ] }; }; exports.check = function(window) { expect(window.main.foo.hello).to.contain('foo'); expect(window.main.bar.hello).to.contain('bar'); }; ================================================ FILE: test/autotests/modules/mask-define-vanilla-js/define-global.js ================================================ window.define = function() { }; ================================================ FILE: test/autotests/modules/mask-define-vanilla-js/library.js ================================================ if (typeof define === 'undefined') { window.defineFound = false; } else { window.defineFound = true; } ================================================ FILE: test/autotests/modules/mask-define-vanilla-js/main.js ================================================ exports.filename = __filename; window.main = module.exports; ================================================ FILE: test/autotests/modules/mask-define-vanilla-js/package.json ================================================ { "name": "autotest", "version": "1.0.0" } ================================================ FILE: test/autotests/modules/mask-define-vanilla-js/test.js ================================================ var expect = require('chai').expect; var path = require('path'); exports.getLassoOptions = function(dir) { return { dependencies: [ { "path": path.join(__dirname, 'define-global.js'), "mask-define": true }, { "path": path.join(__dirname, 'library.js'), "mask-define": true }, 'require-run: ./main' ] }; }; exports.check = function(window) { expect(window.defineFound).to.equal(false); }; ================================================ FILE: test/autotests/modules/missing-module/a.js ================================================ require('./b'); ================================================ FILE: test/autotests/modules/missing-module/b.js ================================================ require('./c'); ================================================ FILE: test/autotests/modules/missing-module/main.js ================================================ require('./a'); ================================================ FILE: test/autotests/modules/missing-module/package.json ================================================ { "name": "autotest", "version": "1.0.0" } ================================================ FILE: test/autotests/modules/missing-module/test.js ================================================ var expect = require('chai').expect; exports.getLassoOptions = function(dir) { return { dependencies: [ 'require-run: ./main' ] }; }; exports.checkError = function(e) { var errorString = e.toString(); expect(errorString).to.contain('Failed to walk dependency [require: ./b]'); expect(errorString).to.contain('Dependency chain: [require: ./main] → [require: ./a] → [require: ./b]'); expect(errorString).to.contain('Error: Module not found: ./c (from "test/autotests/modules/missing-module" and referenced in "test/autotests/modules/missing-module/b.js")'); }; ================================================ FILE: test/autotests/modules/no-conflict/foo.js ================================================ module.exports = { filename: __filename, FOO: true }; ================================================ FILE: test/autotests/modules/no-conflict/main.js ================================================ exports.filename = __filename; exports.foo = require('./foo'); window.main = module.exports; ================================================ FILE: test/autotests/modules/no-conflict/package.json ================================================ { "name": "autotest", "version": "1.0.0" } ================================================ FILE: test/autotests/modules/no-conflict/test.js ================================================ var expect = require('chai').expect; exports.getLassoConfig = function(dir) { return { bundlingEnabled: false, fingerprintsEnabled: false, noConflict: 'myapp' }; }; exports.getLassoOptions = function(dir) { return { dependencies: [ 'require-run: ./main' ] }; }; exports.check = function(window) { expect(window.main.filename).to.contain('main'); expect(window.main.foo.filename).to.contain('foo'); expect(window.main.foo.FOO).to.equal(true); expect(window.$_mod).to.equal(undefined); expect(typeof window.$_mod_myapp).to.equal('object'); }; ================================================ FILE: test/autotests/modules/registerRequireExtension-getDependencies/extra.js ================================================ window.EXTRA = true; ================================================ FILE: test/autotests/modules/registerRequireExtension-getDependencies/hello.foo ================================================ hello ================================================ FILE: test/autotests/modules/registerRequireExtension-getDependencies/main.js ================================================ exports.filename = __filename; exports.hello = require('./hello.foo'); exports.world = require('./world.foo'); window.main = module.exports; ================================================ FILE: test/autotests/modules/registerRequireExtension-getDependencies/package.json ================================================ { "name": "autotest", "version": "1.0.0" } ================================================ FILE: test/autotests/modules/registerRequireExtension-getDependencies/test.js ================================================ var expect = require('chai').expect; var fs = require('fs'); exports.getLassoConfig = function(dir) { return { bundlingEnabled: false, fingerprintsEnabled: false, plugins: [ function myPlugin(lasso, pluginConfig) { lasso.dependencies.registerRequireExtension( 'foo', { async getDependencies (lassoContext) { return [ require.resolve('./extra.js') ]; }, async read (path, lassoContext) { var src = fs.readFileSync(path, { encoding: 'utf8' }); return 'exports.FOO = ' + JSON.stringify(src) + '; exports.filename = __filename;'; } }); } ] }; }; exports.getLassoOptions = function(dir) { return { dependencies: [ 'require-run: ./main' ] }; }; exports.check = function(window) { expect(window.main.filename).to.contain('main'); expect(window.main.hello.FOO).to.equal('hello'); expect(window.main.hello.filename).to.contain('hello.foo'); expect(window.main.world.FOO).to.equal('world'); expect(window.main.world.filename).to.contain('world.foo'); expect(window.EXTRA).to.equal(true); }; ================================================ FILE: test/autotests/modules/registerRequireExtension-getDependencies/world.foo ================================================ world ================================================ FILE: test/autotests/modules/registerRequireType/hello.foo ================================================ hello ================================================ FILE: test/autotests/modules/registerRequireType/main.js ================================================ exports.filename = __filename; var helloPath = './hello.foo'; exports.hello = require(helloPath); exports.world = require('./world.foo'); window.main = module.exports; ================================================ FILE: test/autotests/modules/registerRequireType/package.json ================================================ { "name": "autotest", "version": "1.0.0" } ================================================ FILE: test/autotests/modules/registerRequireType/test.js ================================================ var expect = require('chai').expect; var fs = require('fs'); exports.getLassoConfig = function(dir) { return { bundlingEnabled: false, fingerprintsEnabled: false, plugins: [ function myPlugin(lasso, pluginConfig) { lasso.dependencies.registerRequireType( 'foo', { properties: { 'path': 'string' }, async init (lassoContext) { if (!this.path) { throw new Error('"path" is required for a Marko dependency'); } this.path = this.resolvePath(this.path); }, read (lassoContext) { var src = fs.readFileSync(this.path, { encoding: 'utf8' }); return 'exports.FOO = ' + JSON.stringify(src) + '; exports.filename = __filename;'; } }); } ] }; }; exports.getLassoOptions = function(dir) { return { dependencies: [ 'require-run: ./main', "./hello.foo" ] }; }; exports.check = function(window) { expect(window.main.filename).to.contain('main'); expect(window.main.hello.FOO).to.equal('hello'); expect(window.main.hello.filename).to.contain('hello.foo'); expect(window.main.world.FOO).to.equal('world'); expect(window.main.world.filename).to.contain('world.foo'); }; ================================================ FILE: test/autotests/modules/registerRequireType/world.foo ================================================ world ================================================ FILE: test/autotests/modules/registerRequireType-getDependencies-callback/extra.js ================================================ window.EXTRA = true; ================================================ FILE: test/autotests/modules/registerRequireType-getDependencies-callback/hello.foo ================================================ hello ================================================ FILE: test/autotests/modules/registerRequireType-getDependencies-callback/main.js ================================================ exports.filename = __filename; var helloPath = './hello.foo'; exports.hello = require(helloPath); exports.world = require('./world.foo'); window.main = module.exports; ================================================ FILE: test/autotests/modules/registerRequireType-getDependencies-callback/package.json ================================================ { "name": "autotest", "version": "1.0.0" } ================================================ FILE: test/autotests/modules/registerRequireType-getDependencies-callback/test.js ================================================ var expect = require('chai').expect; var fs = require('fs'); exports.getLassoConfig = function(dir) { return { bundlingEnabled: false, fingerprintsEnabled: false, plugins: [ function myPlugin(lasso, pluginConfig) { lasso.dependencies.registerRequireType( 'foo', { properties: { 'path': 'string' }, async init (lassoContext) { if (!this.path) { throw new Error('"path" is required for a Marko dependency'); } this.path = this.resolvePath(this.path); }, async getDependencies (lassoContext) { return [require.resolve('./extra.js')]; }, read (lassoContext) { var src = fs.readFileSync(this.path, { encoding: 'utf8' }); return 'exports.FOO = ' + JSON.stringify(src) + '; exports.filename = __filename;'; } }); } ] }; }; exports.getLassoOptions = function(dir) { return { dependencies: [ 'require-run: ./main', "./hello.foo" ] }; }; exports.check = function(window) { expect(window.main.filename).to.contain('main'); expect(window.main.hello.FOO).to.equal('hello'); expect(window.main.hello.filename).to.contain('hello.foo'); expect(window.main.world.FOO).to.equal('world'); expect(window.main.world.filename).to.contain('world.foo'); expect(window.EXTRA).to.equal(true); }; ================================================ FILE: test/autotests/modules/registerRequireType-getDependencies-callback/world.foo ================================================ world ================================================ FILE: test/autotests/modules/registerRequireType-getDependencies-promise/extra.js ================================================ window.EXTRA = true; ================================================ FILE: test/autotests/modules/registerRequireType-getDependencies-promise/hello.foo ================================================ hello ================================================ FILE: test/autotests/modules/registerRequireType-getDependencies-promise/main.js ================================================ exports.filename = __filename; var helloPath = './hello.foo'; exports.hello = require(helloPath); exports.world = require('./world.foo'); window.main = module.exports; ================================================ FILE: test/autotests/modules/registerRequireType-getDependencies-promise/package.json ================================================ { "name": "autotest", "version": "1.0.0" } ================================================ FILE: test/autotests/modules/registerRequireType-getDependencies-promise/test.js ================================================ var expect = require('chai').expect; var fs = require('fs'); exports.getLassoConfig = function(dir) { return { bundlingEnabled: false, fingerprintsEnabled: false, plugins: [ function myPlugin(lasso, pluginConfig) { lasso.dependencies.registerRequireType( 'foo', { properties: { 'path': 'string' }, async init (lassoContext) { if (!this.path) { throw new Error('"path" is required for a Marko dependency'); } this.path = this.resolvePath(this.path); }, async getDependencies (lassoContext) { return [ require.resolve('./extra.js') ]; }, read (lassoContext) { var src = fs.readFileSync(this.path, { encoding: 'utf8' }); return 'exports.FOO = ' + JSON.stringify(src) + '; exports.filename = __filename;'; } }); } ] }; }; exports.getLassoOptions = function(dir) { return { dependencies: [ 'require-run: ./main', "./hello.foo" ] }; }; exports.check = function(window) { expect(window.main.filename).to.contain('main'); expect(window.main.hello.FOO).to.equal('hello'); expect(window.main.hello.filename).to.contain('hello.foo'); expect(window.main.world.FOO).to.equal('world'); expect(window.main.world.filename).to.contain('world.foo'); expect(window.EXTRA).to.equal(true); }; ================================================ FILE: test/autotests/modules/registerRequireType-getDependencies-promise/world.foo ================================================ world ================================================ FILE: test/autotests/modules/registerRequireType-getDependencies-value/extra.js ================================================ window.EXTRA = true; ================================================ FILE: test/autotests/modules/registerRequireType-getDependencies-value/hello.foo ================================================ hello ================================================ FILE: test/autotests/modules/registerRequireType-getDependencies-value/main.js ================================================ exports.filename = __filename; var helloPath = './hello.foo'; exports.hello = require(helloPath); exports.world = require('./world.foo'); window.main = module.exports; ================================================ FILE: test/autotests/modules/registerRequireType-getDependencies-value/package.json ================================================ { "name": "autotest", "version": "1.0.0" } ================================================ FILE: test/autotests/modules/registerRequireType-getDependencies-value/test.js ================================================ 'use strict'; var expect = require('chai').expect; var fs = require('fs'); exports.getLassoConfig = function(dir) { return { bundlingEnabled: false, fingerprintsEnabled: false, plugins: [ function myPlugin(lasso, pluginConfig) { lasso.dependencies.registerRequireType( 'foo', { properties: { 'path': 'string' }, async init (lassoContext) { if (!this.path) { throw new Error('"path" is required for a Marko dependency'); } this.path = this.resolvePath(this.path); this.foo = true; }, async getDependencies (lassoContext) { expect(this.foo).to.equal(true); return [ require.resolve('./extra.js') ]; }, read (lassoContext) { var src = fs.readFileSync(this.path, { encoding: 'utf8' }); return 'exports.FOO = ' + JSON.stringify(src) + '; exports.filename = __filename;'; } }); } ] }; }; exports.getLassoOptions = function(dir) { return { dependencies: [ './hello.foo', 'require-run: ./main', ] }; }; exports.check = function(window) { expect(window.main.filename).to.contain('main'); expect(window.main.hello.FOO).to.equal('hello'); expect(window.main.hello.filename).to.contain('hello.foo'); expect(window.main.world.FOO).to.equal('world'); expect(window.main.world.filename).to.contain('world.foo'); expect(window.EXTRA).to.equal(true); }; ================================================ FILE: test/autotests/modules/registerRequireType-getDependencies-value/world.foo ================================================ world ================================================ FILE: test/autotests/modules/require-builtin-core/main.js ================================================ exports.assert = require('assert'); exports.buffer = require('buffer'); exports.events = require('events'); exports.path = require('path'); exports.stream = require('stream'); exports.util = require('util'); window.main = exports; ================================================ FILE: test/autotests/modules/require-builtin-core/package.json ================================================ { "name": "autotest", "version": "1.0.0" } ================================================ FILE: test/autotests/modules/require-builtin-core/test.js ================================================ var expect = require('chai').expect; exports.getLassoOptions = function(dir) { return { dependencies: [ 'require-run: ./main' ] }; }; exports.check = function(window) { expect(window.main.assert.ok).to.be.a('function'); }; ================================================ FILE: test/autotests/modules/require-css/foo.css ================================================ .foo { } ================================================ FILE: test/autotests/modules/require-css/main.js ================================================ require('./foo.css'); exports.filename = __filename; window.main = module.exports; ================================================ FILE: test/autotests/modules/require-css/package.json ================================================ { "name": "autotest", "version": "1.0.0" } ================================================ FILE: test/autotests/modules/require-css/test.js ================================================ var expect = require('chai').expect; exports.getLassoOptions = function(dir) { return { dependencies: [ 'require-run: ./main' ] }; }; exports.check = function(window) { expect(window.main.filename).to.contain('main'); }; ================================================ FILE: test/autotests/modules/require-custom-ext/hello.foo ================================================ module.exports = { filename: __filename, FOO: true }; ================================================ FILE: test/autotests/modules/require-custom-ext/main.js ================================================ exports.filename = __filename; exports.foo = require('./hello'); exports.foo2 = require('./hello.foo'); window.main = module.exports; ================================================ FILE: test/autotests/modules/require-custom-ext/package.json ================================================ { "name": "autotest", "version": "1.0.0" } ================================================ FILE: test/autotests/modules/require-custom-ext/require-foo-plugin.js ================================================ const fs = require('fs'); module.exports = exports = function(lasso, config) { lasso.dependencies.registerRequireExtension( 'foo', { async read (path, lassoContext) { let src = await fs.promises.readFile(path, 'utf8'); src = src.replace(/FOO/g, 'BAR'); return src; }, async lastModified (path, lassoContext) { return lassoContext.getFileLastModified(path); } }); }; module.exports.counter = 0; ================================================ FILE: test/autotests/modules/require-custom-ext/test.js ================================================ var expect = require('chai').expect; exports.getLassoConfig = function() { return { bundlingEnabled: false, fingerprintsEnabled: false, plugins: [ require('./require-foo-plugin') ] }; }; exports.getLassoOptions = function(dir) { return { dependencies: [ 'require-run: ./main' ] }; }; exports.check = function(window) { expect(window.main.filename).to.contain('main'); expect(window.main.foo.filename).to.contain('foo'); expect(window.main.foo.BAR).to.equal(true); }; ================================================ FILE: test/autotests/modules/require-custom-ext-no-plugin/hello.foo ================================================ module.exports = { filename: __filename, FOO: true }; ================================================ FILE: test/autotests/modules/require-custom-ext-no-plugin/main.js ================================================ exports.filename = __filename; exports.foo = require('./hello'); exports.foo2 = require('./hello.foo'); window.main = module.exports; ================================================ FILE: test/autotests/modules/require-custom-ext-no-plugin/package.json ================================================ { "name": "autotest", "version": "1.0.0" } ================================================ FILE: test/autotests/modules/require-custom-ext-no-plugin/test.js ================================================ var expect = require('chai').expect; exports.getLassoConfig = function() { return { bundlingEnabled: false, fingerprintsEnabled: false, require: { extensions: ['.js', '.foo'] } }; }; exports.getLassoOptions = function(dir) { return { dependencies: [ 'require-run: ./main' ] }; }; exports.check = function(window) { expect(window.main.filename).to.contain('main'); expect(window.main.foo).to.equal(window.main.foo2); expect(window.main.foo.filename).to.contain('hello.foo'); }; ================================================ FILE: test/autotests/modules/require-custom-type/foo.json ================================================ { "hello": "world" } ================================================ FILE: test/autotests/modules/require-custom-type/main.js ================================================ require('foo: ./foo.json'); exports.filename = __filename; window.main = module.exports; ================================================ FILE: test/autotests/modules/require-custom-type/package.json ================================================ { "name": "autotest", "version": "1.0.0" } ================================================ FILE: test/autotests/modules/require-custom-type/test.js ================================================ var expect = require('chai').expect; function fooPlugins(lasso, config) { lasso.dependencies.registerJavaScriptType('foo', { properties: { 'path': 'string' }, async init (context) { if (!this.path) { throw new Error('"path" is required'); } this.path = this.resolvePath(this.path); }, // Read the resource: read (context) { return 'var foo = true;'; } }); } exports.getLassoConfig = function() { return { bundlingEnabled: false, fingerprintsEnabled: false, plugins: [ fooPlugins ] }; }; exports.getLassoOptions = function(dir) { return { dependencies: [ 'require-run: ./main' ] }; }; exports.check = function(window) { expect(window.main.filename).to.contain('main'); }; ================================================ FILE: test/autotests/modules/require-globals/main.js ================================================ require('foo'); exports.filename = __filename; exports.foo = window.foo; window.main = module.exports; ================================================ FILE: test/autotests/modules/require-globals/package.json ================================================ { "name": "autotest", "version": "1.0.0" } ================================================ FILE: test/autotests/modules/require-globals/test.js ================================================ var expect = require('chai').expect; exports.getLassoConfig = function(dir) { var globals = {}; globals[require.resolve('foo')] = 'foo'; return { bundlingEnabled: false, fingerprintsEnabled: false, require: { globals } }; }; exports.getLassoOptions = function(dir) { return { dependencies: [ 'require-run: ./main' ] }; }; exports.check = function(window) { expect(window.main.filename).to.contain('main'); expect(window.foo.FOO).to.equal(true); expect(window.main.foo.FOO).to.equal(true); }; ================================================ FILE: test/autotests/modules/require-installed/main.js ================================================ exports.filename = __filename; exports.foo = require('foo'); window.main = module.exports; ================================================ FILE: test/autotests/modules/require-installed/package.json ================================================ { "name": "autotest", "version": "1.0.0" } ================================================ FILE: test/autotests/modules/require-installed/test.js ================================================ var expect = require('chai').expect; exports.getLassoOptions = function(dir) { return { dependencies: [ 'require-run: ./main' ] }; }; exports.check = function(window) { expect(window.main.filename).to.contain('main'); expect(window.main.foo.filename).to.contain('foo'); expect(window.main.foo.FOO).to.equal(true); }; ================================================ FILE: test/autotests/modules/require-installed-scoped-package/main.js ================================================ exports.filename = __filename; exports.bar = require('@foo/bar'); window.main = module.exports; ================================================ FILE: test/autotests/modules/require-installed-scoped-package/package.json ================================================ { "name": "autotest", "version": "1.0.0" } ================================================ FILE: test/autotests/modules/require-installed-scoped-package/test.js ================================================ var expect = require('chai').expect; exports.getLassoOptions = function(dir) { return { dependencies: [ 'require-run: ./main' ] }; }; exports.check = function(window) { expect(window.main.filename).to.contain('main'); expect(window.main.bar.filename).to.contain('@foo/bar'); expect(window.main.bar.SCOPED_BAR).to.equal(true); }; ================================================ FILE: test/autotests/modules/require-json/foo.json ================================================ { "FOO": true } ================================================ FILE: test/autotests/modules/require-json/main.js ================================================ exports.filename = __filename; exports.foo1 = require('./foo'); exports.foo2 = require('./foo.json'); window.main = module.exports; ================================================ FILE: test/autotests/modules/require-json/package.json ================================================ { "name": "autotest", "version": "1.0.0" } ================================================ FILE: test/autotests/modules/require-json/test.js ================================================ var expect = require('chai').expect; exports.getLassoOptions = function(dir) { return { dependencies: [ 'require-run: ./main' ] }; }; exports.check = function(window) { expect(window.main.filename).to.contain('main'); expect(window.main.foo1.FOO).to.equal(true); expect(window.main.foo2.FOO).to.equal(true); }; ================================================ FILE: test/autotests/modules/require-relative/foo.js ================================================ module.exports = { filename: __filename, FOO: true }; ================================================ FILE: test/autotests/modules/require-relative/main.js ================================================ exports.filename = __filename; exports.foo = require('./foo'); window.main = module.exports; ================================================ FILE: test/autotests/modules/require-relative/package.json ================================================ { "name": "autotest", "version": "1.0.0" } ================================================ FILE: test/autotests/modules/require-relative/test.js ================================================ var expect = require('chai').expect; exports.getLassoOptions = function(dir) { return { dependencies: [ 'require-run: ./main' ] }; }; exports.check = function(window) { expect(window.main.filename).to.contain('main'); expect(window.main.foo.filename).to.contain('foo'); expect(window.main.foo.FOO).to.equal(true); }; ================================================ FILE: test/autotests/modules/require-remap-flag/bar-desktop.js ================================================ exports.isDesktop = true; ================================================ FILE: test/autotests/modules/require-remap-flag/bar.js ================================================ exports.isDesktop = false; ================================================ FILE: test/autotests/modules/require-remap-flag/browser.json ================================================ { "requireRemap": [ { "from": "./foo.js", "to": "./foo-mobile.js", "if-flag": "mobile" }, { "from": "./bar.js", "to": "./bar-desktop.js", "if-flag": "desktop" } ] } ================================================ FILE: test/autotests/modules/require-remap-flag/foo-mobile.js ================================================ exports.isMobile = true; ================================================ FILE: test/autotests/modules/require-remap-flag/foo.js ================================================ exports.isMobile = false; ================================================ FILE: test/autotests/modules/require-remap-flag/main.js ================================================ exports.filename = __filename; exports.foo = require('./foo'); exports.bar = require('./bar'); window.main = module.exports; ================================================ FILE: test/autotests/modules/require-remap-flag/package.json ================================================ { "name": "autotest", "version": "1.0.0" } ================================================ FILE: test/autotests/modules/require-remap-flag/test.js ================================================ var expect = require('chai').expect; exports.getLassoOptions = function(dir) { return { dependencies: [ 'require-run: ./main' ], flags: ['mobile'] }; }; exports.check = function(window) { expect(window.main.filename).to.contain('main'); expect(window.main.foo.isMobile).to.equal(true); expect(window.main.bar.isDesktop).to.equal(false); }; ================================================ FILE: test/autotests/modules/require-remap-flag-caching/bar-desktop.js ================================================ exports.isDesktop = true; ================================================ FILE: test/autotests/modules/require-remap-flag-caching/bar.js ================================================ exports.isDesktop = false; ================================================ FILE: test/autotests/modules/require-remap-flag-caching/browser.json ================================================ { "requireRemap": [ { "from": "./foo.js", "to": "./foo-mobile.js", "if-flag": "mobile" }, { "from": "./bar.js", "to": "./bar-desktop.js", "if-flag": "desktop" } ] } ================================================ FILE: test/autotests/modules/require-remap-flag-caching/foo-mobile.js ================================================ exports.isMobile = true; ================================================ FILE: test/autotests/modules/require-remap-flag-caching/foo.js ================================================ exports.isMobile = false; ================================================ FILE: test/autotests/modules/require-remap-flag-caching/main.js ================================================ exports.filename = __filename; exports.foo = require('./foo'); exports.bar = require('./bar'); window.main = module.exports; ================================================ FILE: test/autotests/modules/require-remap-flag-caching/package.json ================================================ { "name": "autotest", "version": "1.0.0" } ================================================ FILE: test/autotests/modules/require-remap-flag-caching/test.js ================================================ var expect = require('chai').expect; exports.tests = [ { lassoOptions: { dependencies: [ 'require-run: ./main' ], flags: ['mobile'] }, check: function(window) { expect(window.main.filename).to.contain('main'); expect(window.main.foo.isMobile).to.equal(true); expect(window.main.bar.isDesktop).to.equal(false); } }, { lassoOptions: { dependencies: [ 'require-run: ./main' ], flags: ['desktop'] }, check: function(window) { expect(window.main.filename).to.contain('main'); expect(window.main.foo.isMobile).to.equal(false); expect(window.main.bar.isDesktop).to.equal(true); } } ]; ================================================ FILE: test/autotests/modules/require-remap-local-to-installed/foo.js ================================================ module.exports = { filename: __filename, FOO: true }; ================================================ FILE: test/autotests/modules/require-remap-local-to-installed/main.js ================================================ exports.filename = __filename; exports.foo = require('./foo'); window.main = module.exports; ================================================ FILE: test/autotests/modules/require-remap-local-to-installed/package.json ================================================ { "name": "autotest", "version": "1.0.0", "browser": { "./foo.js": "foo-installed" } } ================================================ FILE: test/autotests/modules/require-remap-local-to-installed/test.js ================================================ var expect = require('chai').expect; exports.getLassoOptions = function(dir) { return { dependencies: [ 'require-run: ./main' ] }; }; exports.check = function(window) { expect(window.main.filename).to.contain('main'); expect(window.main.foo.filename).to.contain('foo-installed'); expect(window.main.foo.FOO_INSTALLED).to.equal(true); }; ================================================ FILE: test/autotests/modules/require-remap-local-to-local/foo-browser.js ================================================ module.exports = { filename: __filename, FOO_BROWSER: true }; ================================================ FILE: test/autotests/modules/require-remap-local-to-local/foo.js ================================================ module.exports = { filename: __filename, FOO: true }; ================================================ FILE: test/autotests/modules/require-remap-local-to-local/main.js ================================================ exports.filename = __filename; exports.foo = require('./foo'); window.main = module.exports; ================================================ FILE: test/autotests/modules/require-remap-local-to-local/package.json ================================================ { "name": "autotest", "version": "1.0.0", "browser": { "foo.js": "./foo-browser" } } ================================================ FILE: test/autotests/modules/require-remap-local-to-local/test.js ================================================ var expect = require('chai').expect; exports.getLassoOptions = function(dir) { return { dependencies: [ 'require-run: ./main' ] }; }; exports.check = function(window) { expect(window.main.filename).to.contain('main'); expect(window.main.foo.filename).to.contain('foo-browser'); expect(window.main.foo.FOO_BROWSER).to.equal(true); }; ================================================ FILE: test/autotests/modules/require-remap-void-installed/main.js ================================================ exports.filename = __filename; exports.foo = require('foo-installed'); window.main = module.exports; ================================================ FILE: test/autotests/modules/require-remap-void-installed/package.json ================================================ { "name": "autotest", "version": "1.0.0", "browser": { "foo-installed": false } } ================================================ FILE: test/autotests/modules/require-remap-void-installed/test.js ================================================ var expect = require('chai').expect; exports.getLassoOptions = function(dir) { return { dependencies: [ 'require-run: ./main' ] }; }; exports.check = function(window) { expect(window.main.filename).to.contain('main'); expect(Object.keys(window.main.foo).length).to.equal(0); }; ================================================ FILE: test/autotests/modules/require-remap-void-relative/foo.js ================================================ module.exports = { filename: __filename, FOO: true }; ================================================ FILE: test/autotests/modules/require-remap-void-relative/foo2.js ================================================ module.exports = { filename: __filename, FOO: true }; ================================================ FILE: test/autotests/modules/require-remap-void-relative/main.js ================================================ exports.filename = __filename; exports.foo = require('./foo'); exports.foo2 = require('./foo2'); window.main = module.exports; ================================================ FILE: test/autotests/modules/require-remap-void-relative/package.json ================================================ { "name": "autotest", "version": "1.0.0", "browser": { "foo.js": false, "./foo2": false } } ================================================ FILE: test/autotests/modules/require-remap-void-relative/test.js ================================================ var expect = require('chai').expect; exports.getLassoOptions = function(dir) { return { dependencies: [ 'require-run: ./main' ] }; }; exports.check = function(window) { expect(window.main.filename).to.contain('main'); expect(Object.keys(window.main.foo).length).to.equal(0); expect(Object.keys(window.main.foo2).length).to.equal(0); }; ================================================ FILE: test/autotests/modules/require-resolve/foo.js ================================================ module.exports = { filename: __filename, FOO: true }; ================================================ FILE: test/autotests/modules/require-resolve/main.js ================================================ exports.filename = __filename; exports.fooPath = require.resolve('./foo'); exports.foo1 = require('./foo'); exports.foo2 = require(require.resolve('./foo')); window.main = module.exports; ================================================ FILE: test/autotests/modules/require-resolve/package.json ================================================ { "name": "autotest", "version": "1.0.0" } ================================================ FILE: test/autotests/modules/require-resolve/test.js ================================================ var expect = require('chai').expect; exports.getLassoOptions = function(dir) { return { dependencies: [ 'require-run: ./main' ] }; }; exports.check = function(window) { expect(window.main.filename).to.contain('main'); expect(window.main.fooPath).to.contain('foo'); expect(window.main.foo1).to.equal(window.main.foo2); }; ================================================ FILE: test/autotests/modules/require-virtual-module/main.js ================================================ exports.filename = __filename; var virtualModulePath = '/virtual-module/something.foo'; exports.foo = require(virtualModulePath); window.main = module.exports; ================================================ FILE: test/autotests/modules/require-virtual-module/package.json ================================================ { "name": "autotest", "version": "1.0.0" } ================================================ FILE: test/autotests/modules/require-virtual-module/test.js ================================================ var expect = require('chai').expect; exports.getLassoOptions = function(dir) { return { dependencies: [ 'require-run: ./main', { 'type': 'require', virtualModule: { path: __dirname + '/something.foo', clientPath: '/virtual-module/something.foo', read (lassoContext) { return 'exports.hello = "world"; exports.filename = __filename;'; } } } ] }; }; exports.check = function(window) { expect(window.main.filename).to.contain('main'); expect(window.main.foo.hello).to.equal('world'); expect(window.main.foo.filename).to.equal('/virtual-module/something.foo'); }; ================================================ FILE: test/autotests/modules/requireRemap-not-conditional/browser.json ================================================ { "requireRemap": [ { "from": "./foo.js", "to": "./foo-browser.js" } ] } ================================================ FILE: test/autotests/modules/requireRemap-not-conditional/foo-browser.js ================================================ exports.isBrowser = true; ================================================ FILE: test/autotests/modules/requireRemap-not-conditional/foo.js ================================================ exports.isBrowser = false; ================================================ FILE: test/autotests/modules/requireRemap-not-conditional/main.js ================================================ exports.filename = __filename; exports.foo = require('./foo'); window.main = module.exports; ================================================ FILE: test/autotests/modules/requireRemap-not-conditional/package.json ================================================ { "name": "autotest", "version": "1.0.0" } ================================================ FILE: test/autotests/modules/requireRemap-not-conditional/test.js ================================================ var expect = require('chai').expect; exports.getLassoOptions = function(dir) { return { dependencies: [ 'require-run: ./main' ], flags: ['mobile'] }; }; exports.check = function(window) { expect(window.main.filename).to.contain('main'); expect(window.main.foo.isBrowser).to.equal(true); }; ================================================ FILE: test/autotests/modules/requireRemap-plus-package-browser/browser.json ================================================ { "requireRemap": [ { "from": "./foo.js", "to": "./foo-browser-lasso.js" } ] } ================================================ FILE: test/autotests/modules/requireRemap-plus-package-browser/foo-browser-lasso.js ================================================ exports.isBrowserLasso = true; ================================================ FILE: test/autotests/modules/requireRemap-plus-package-browser/foo-browser.js ================================================ exports.isBrowser = true; ================================================ FILE: test/autotests/modules/requireRemap-plus-package-browser/foo.js ================================================ exports.isBrowser = false; ================================================ FILE: test/autotests/modules/requireRemap-plus-package-browser/main.js ================================================ exports.filename = __filename; exports.foo = require('./foo'); window.main = module.exports; ================================================ FILE: test/autotests/modules/requireRemap-plus-package-browser/package.json ================================================ { "name": "autotest", "version": "1.0.0", "browser": { "./foo.js": "./foo-browser.js" } } ================================================ FILE: test/autotests/modules/requireRemap-plus-package-browser/test.js ================================================ var expect = require('chai').expect; exports.getLassoOptions = function(dir) { return { dependencies: [ 'require-run: ./main' ], flags: ['mobile'] }; }; exports.check = function(window) { expect(window.main.filename).to.contain('main'); expect(window.main.foo.isBrowserLasso).to.equal(true); }; ================================================ FILE: test/autotests/modules/simple/main.js ================================================ exports.filename = __filename; window.main = module.exports; ================================================ FILE: test/autotests/modules/simple/package.json ================================================ { "name": "autotest", "version": "1.0.0" } ================================================ FILE: test/autotests/modules/simple/test.js ================================================ var expect = require('chai').expect; exports.getLassoOptions = function(dir) { return { dependencies: [ 'require-run: ./main' ] }; }; exports.check = function(window) { expect(window.main.filename).to.contain('main'); }; ================================================ FILE: test/autotests/plugins/custom-dependency-type-callback/browser.json ================================================ { "dependencies": [ { "type": "foo-js", "path": "./foo.js" }, { "type": "foo-css", "path": "./foo.css" } ] } ================================================ FILE: test/autotests/plugins/custom-dependency-type-callback/foo.css ================================================ hello world ================================================ FILE: test/autotests/plugins/custom-dependency-type-callback/foo.js ================================================ hello world ================================================ FILE: test/autotests/plugins/custom-dependency-type-callback/package.json ================================================ { "name": "autotest", "version": "0.0.0" } ================================================ FILE: test/autotests/plugins/custom-dependency-type-callback/plugin.js ================================================ const fs = require('fs'); module.exports = exports = function(lasso, config) { lasso.dependencies.registerJavaScriptType( 'foo-js', { properties: { 'path': 'string' }, async init () { if (!this.path) { throw new Error('"path" is required for a less dependency'); } this.path = this.resolvePath(this.path); }, async read (lassoContext) { module.exports.jsCounter++; const src = await fs.promises.readFile(this.path, 'utf8'); return src.toUpperCase(); }, getSourceFile: function() { return this.path; }, async lastModified (lassoContext) { return -1; } }); lasso.dependencies.registerStyleSheetType( 'foo-css', { properties: { 'path': 'string' }, async init () { if (!this.path) { throw new Error('"path" is required for a less dependency'); } this.path = this.resolvePath(this.path); }, async read (lassoContext) { module.exports.cssCounter++; let src = await fs.promises.readFile(this.path, 'utf8'); src = src.split('').reverse().join(''); return src; }, getSourceFile: function() { return this.path; }, async lastModified (lassoContext) { return -1; } }); }; module.exports.jsCounter = 0; module.exports.cssCounter = 0; ================================================ FILE: test/autotests/plugins/custom-dependency-type-callback/test.js ================================================ var expect = require('chai').expect; exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundlingEnabled: true, plugins: [ require('./plugin') ] }; }; exports.getLassoOptions = function() { return { dependencies: [ './browser.json' ] }; }; exports.check = function(lassoPageResult, writerTracker) { var plugin = require('./plugin'); var jsCode = writerTracker.getCodeForPath(lassoPageResult.getJavaScriptFiles()[0]); expect(jsCode).to.equal('HELLO WORLD'); var cssCode = writerTracker.getCodeForPath(lassoPageResult.getCSSFiles()[0]); expect(cssCode).to.equal('dlrow olleh'); expect(plugin.cssCounter).to.equal(1); expect(plugin.jsCounter).to.equal(1); }; ================================================ FILE: test/autotests/plugins/custom-dependency-type-promise/browser.json ================================================ { "dependencies": [ { "type": "foo-js", "path": "./foo.js" }, { "type": "foo-css", "path": "./foo.css" } ] } ================================================ FILE: test/autotests/plugins/custom-dependency-type-promise/foo.css ================================================ hello world ================================================ FILE: test/autotests/plugins/custom-dependency-type-promise/foo.js ================================================ hello world ================================================ FILE: test/autotests/plugins/custom-dependency-type-promise/package.json ================================================ { "name": "autotest", "version": "0.0.0" } ================================================ FILE: test/autotests/plugins/custom-dependency-type-promise/plugin.js ================================================ const fs = require('fs'); module.exports = exports = function(lasso, config) { lasso.dependencies.registerJavaScriptType( 'foo-js', { properties: { 'path': 'string' }, async init () { if (!this.path) { throw new Error('"path" is required for a less dependency'); } this.path = this.resolvePath(this.path); }, async read (lassoContext) { module.exports.jsCounter++; const src = await fs.promises.readFile(this.path, 'utf8'); return src.toUpperCase(); }, getSourceFile: function() { return this.path; }, async lastModified (lassoContext) { return -1; } }); lasso.dependencies.registerStyleSheetType( 'foo-css', { properties: { 'path': 'string' }, async init () { if (!this.path) { throw new Error('"path" is required for a less dependency'); } this.path = this.resolvePath(this.path); }, async read (lassoContext) { module.exports.cssCounter++; let src = await fs.promises.readFile(this.path, 'utf8'); src = src.split('').reverse().join(''); return src; }, getSourceFile: function() { return this.path; }, async lastModified (lassoContext) { return -1; } }); }; module.exports.jsCounter = 0; module.exports.cssCounter = 0; ================================================ FILE: test/autotests/plugins/custom-dependency-type-promise/test.js ================================================ var expect = require('chai').expect; exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundlingEnabled: true, plugins: [ require('./plugin') ] }; }; exports.getLassoOptions = function() { return { dependencies: [ './browser.json' ] }; }; exports.check = function(lassoPageResult, writerTracker) { var plugin = require('./plugin'); var jsCode = writerTracker.getCodeForPath(lassoPageResult.getJavaScriptFiles()[0]); expect(jsCode).to.equal('HELLO WORLD'); var cssCode = writerTracker.getCodeForPath(lassoPageResult.getCSSFiles()[0]); expect(cssCode).to.equal('dlrow olleh'); expect(plugin.cssCounter).to.equal(1); expect(plugin.jsCounter).to.equal(1); }; ================================================ FILE: test/autotests/plugins/events/async-foo.js ================================================ console.log('async-foo'); ================================================ FILE: test/autotests/plugins/events/browser.json ================================================ { "dependencies": [ "require: ./foo" ], "async": { "foo": [ "./async-foo.js" ] } } ================================================ FILE: test/autotests/plugins/events/expected-events.json ================================================ [ "beforeBuildPage", "beforeAddDependencyToSyncPageBundle:[commonjs-runtime]", "beforeAddDependencyToSyncPageBundle:[commonjs-ready: inline=\"end\"]", "beforeAddDependencyToSyncPageBundle:[commonjs-builtin: name=\"lasso-loader\", target=\"/lasso-loader$x.x.x/src/index\"]", "beforeAddDependencyToSyncPageBundle:[loader-metadata]", "beforeAddDependencyToSyncPageBundle:[commonjs-installed: parentPath=\"/lasso-loader$x.x.x\", childName=\"raptor-util\", childVersion=\"x.x.x\", parentDir=\"/node_modules/lasso-loader\"]", "beforeAddDependencyToSyncPageBundle:[commonjs-def: path=\"/raptor-util$x.x.x/extend\"]", "beforeAddDependencyToSyncPageBundle:[commonjs-def: path=\"/lasso-loader$x.x.x/src/resource-loader\"]", "beforeAddDependencyToSyncPageBundle:[commonjs-installed: parentPath=\"/lasso-loader$x.x.x\", childName=\"events\", childVersion=\"x.x.x\", parentDir=\"/node_modules/lasso-loader\"]", "beforeAddDependencyToSyncPageBundle:[commonjs-main: dir=\"/events$x.x.x\", main=\"events\"]", "beforeAddDependencyToSyncPageBundle:[commonjs-def: path=\"/events$x.x.x/events\"]", "beforeAddDependencyToSyncPageBundle:[commonjs-def: path=\"/lasso-loader$x.x.x/src/index\"]", "beforeAddDependencyToSyncPageBundle:[commonjs-def: path=\"/autotest$x.x.x/foo\"]", "beforeAddDependencyToAsyncPageBundle:[js: path=\"/test/autotests/plugins/events/async-foo.js\"]" ] ================================================ FILE: test/autotests/plugins/events/foo.js ================================================ console.log('foo'); require('lasso-loader').async('foo', function() { }); ================================================ FILE: test/autotests/plugins/events/package.json ================================================ { "name": "autotest", "version": "0.0.0" } ================================================ FILE: test/autotests/plugins/events/plugin.js ================================================ 'use strict'; var events = []; module.exports = exports = function(lasso, config) { lasso.on('beforeBuildPage', (event) => { var context = event.context; events.push('beforeBuildPage'); context.on('beforeAddDependencyToSyncPageBundle', (event) => { var dependency = event.dependency; events.push('beforeAddDependencyToSyncPageBundle:' + dependency); }); context.on('beforeAddDependencyToAsyncPageBundle', (event) => { var dependency = event.dependency; events.push('beforeAddDependencyToAsyncPageBundle:' + dependency); }); }); }; exports.events = events; ================================================ FILE: test/autotests/plugins/events/test.js ================================================ exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundlingEnabled: true, plugins: [ require('./plugin') ] }; }; exports.getLassoOptions = function() { return { dependencies: [ './browser.json' ] }; }; exports.check = function(lassoPageResult, writerTracker, helpers) { var events = require('./plugin').events; helpers.compare(helpers.normalizeOutput(events, { replaceVersions: true }), '-events.json'); }; ================================================ FILE: test/autotests/plugins/global-dependency-prop/package.json ================================================ { "name": "autotest", "version": "0.0.0" } ================================================ FILE: test/autotests/plugins/global-dependency-prop/plugin.js ================================================ module.exports = exports = function(lasso, config) { lasso.dependencies.registerRequireExtension( 'foo', { read: function(filename, lassoContext) { return 'module.exports="FOO"'; } }); }; ================================================ FILE: test/autotests/plugins/global-dependency-prop/something.foo ================================================ ================================================ FILE: test/autotests/plugins/global-dependency-prop/test.js ================================================ var expect = require('chai').expect; exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundlingEnabled: true, plugins: [ require('./plugin') ] }; }; exports.getLassoOptions = function() { return { dependencies: [ 'require: ./something.foo' ] }; }; exports.check = function(lassoPageResult, writerTracker) { var jsCode = writerTracker.getCodeForPath(lassoPageResult.getJavaScriptFiles()[0]); expect(jsCode).to.contain('module.exports="FOO"'); }; ================================================ FILE: test/autotests/plugins/inline-all-for-slot/bar.js ================================================ console.log('bar'); ================================================ FILE: test/autotests/plugins/inline-all-for-slot/baz.js ================================================ console.log('baz'); ================================================ FILE: test/autotests/plugins/inline-all-for-slot/browser.json ================================================ { "dependencies": [ { "path": "./foo.js", "slot": "my-inline-slot" }, { "path": "./bar.js", "slot": "my-inline-slot" }, "./baz.js" ] } ================================================ FILE: test/autotests/plugins/inline-all-for-slot/expected-my-inline-slot.html ================================================ ================================================ FILE: test/autotests/plugins/inline-all-for-slot/foo.js ================================================ console.log('foo'); ================================================ FILE: test/autotests/plugins/inline-all-for-slot/package.json ================================================ { "name": "autotest", "version": "0.0.0" } ================================================ FILE: test/autotests/plugins/inline-all-for-slot/plugin.js ================================================ 'use strict'; var events = []; module.exports = exports = function(lasso, config) { lasso.on('beforeBuildPage', (event) => { var context = event.context; context.on('beforeAddDependencyToSyncPageBundle', (event) => { var dependency = event.dependency; if (event.slot === 'my-inline-slot') { dependency.inline = true; } }); }); }; exports.events = events; ================================================ FILE: test/autotests/plugins/inline-all-for-slot/test.js ================================================ var expect = require('chai').expect; exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundlingEnabled: true, plugins: [ require('./plugin') ] }; }; exports.getLassoOptions = function() { return { dependencies: [ './browser.json' ] }; }; exports.check = function(lassoPageResult, writerTracker, helpers) { var myInlineSlotHtml = lassoPageResult.getSlotHtml('my-inline-slot'); helpers.compare(myInlineSlotHtml, '-my-inline-slot.html'); var jsCode = writerTracker.getCodeForPath(lassoPageResult.getJavaScriptFiles()[0]); expect(jsCode).to.equal("console.log('baz');"); }; ================================================ FILE: test/autotests/plugins/lasso-context-event-bundle-written/browser.json ================================================ { "dependencies": [ "require: ./foo" ] } ================================================ FILE: test/autotests/plugins/lasso-context-event-bundle-written/expected-events.json ================================================ [ "plugins-lasso-context-event-bundle-written", "plugins-lasso-context-event-bundle-written" ] ================================================ FILE: test/autotests/plugins/lasso-context-event-bundle-written/foo.js ================================================ console.log('foo'); ================================================ FILE: test/autotests/plugins/lasso-context-event-bundle-written/package.json ================================================ { "name": "autotest", "version": "0.0.0" } ================================================ FILE: test/autotests/plugins/lasso-context-event-bundle-written/plugin.js ================================================ 'use strict'; var events = []; module.exports = exports = function(lasso, config) { lasso.on('beforeBuildPage', (event) => { var context = event.context; context.on('bundleWritten', (event) => { events.push(event.bundle.name); }); }); }; exports.events = events; ================================================ FILE: test/autotests/plugins/lasso-context-event-bundle-written/test.js ================================================ exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundlingEnabled: true, plugins: [ require('./plugin') ] }; }; exports.getLassoOptions = function() { return { dependencies: [ './browser.json' ] }; }; exports.check = function(lassoPageResult, writerTracker, helpers) { var events = require('./plugin').events; helpers.compare(helpers.normalizeOutput(events, { replaceVersions: true }), '-events.json'); }; ================================================ FILE: test/autotests/plugins/lasso-context-event-resource-written/browser.json ================================================ { "dependencies": [ "./fonts/fonts.css" ] } ================================================ FILE: test/autotests/plugins/lasso-context-event-resource-written/expected-events.json ================================================ [ { "url": "plugins-lasso-context-event-resource-written/autotest$x.x.x/fonts/Aleo-Regular.woff", "outputFile": true, "sourceFile": true } ] ================================================ FILE: test/autotests/plugins/lasso-context-event-resource-written/fonts/fonts.css ================================================ @font-face { src: url('Aleo-Regular.woff'); } ================================================ FILE: test/autotests/plugins/lasso-context-event-resource-written/package.json ================================================ { "name": "autotest", "version": "0.0.0" } ================================================ FILE: test/autotests/plugins/lasso-context-event-resource-written/plugin.js ================================================ 'use strict'; var events = []; module.exports = exports = function(lasso, config) { lasso.on('beforeBuildPage', (event) => { var context = event.context; context.on('resourceWritten', event => { const correctSourceFile = event.sourceFile.endsWith('/test/autotests/plugins/lasso-context-event-resource-written/fonts/Aleo-Regular.woff'); const correctOutputFile = event.outputFile.endsWith('/test/build/plugins-lasso-context-event-resource-written/plugins-lasso-context-event-resource-written/autotest$0.0.0/fonts/Aleo-Regular.woff'); events.push({ url: event.url, sourceFile: correctSourceFile, outputFile: correctOutputFile }); }); }); }; exports.events = events; ================================================ FILE: test/autotests/plugins/lasso-context-event-resource-written/test.js ================================================ exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundlingEnabled: true, plugins: [ require('./plugin') ] }; }; exports.getLassoOptions = function() { return { dependencies: [ './browser.json' ] }; }; exports.check = function(lassoPageResult, writerTracker, helpers) { var events = require('./plugin').events; helpers.compare(helpers.normalizeOutput(events, { replaceVersions: true }), '-events.json'); }; ================================================ FILE: test/autotests/plugins/lasso-writer-event-bundle-written/browser.json ================================================ { "dependencies": [ "require: ./foo" ] } ================================================ FILE: test/autotests/plugins/lasso-writer-event-bundle-written/expected-events.json ================================================ [ "plugins-lasso-writer-event-bundle-written", "plugins-lasso-writer-event-bundle-written" ] ================================================ FILE: test/autotests/plugins/lasso-writer-event-bundle-written/foo.js ================================================ console.log('foo'); ================================================ FILE: test/autotests/plugins/lasso-writer-event-bundle-written/package.json ================================================ { "name": "autotest", "version": "0.0.0" } ================================================ FILE: test/autotests/plugins/lasso-writer-event-bundle-written/plugin.js ================================================ 'use strict'; var events = []; module.exports = exports = function(lasso, config) { lasso.on('beforeBuildPage', (event) => { var context = event.context; context.writer.on('bundleWritten', (event) => { events.push(event.bundle.name); }); }); }; exports.events = events; ================================================ FILE: test/autotests/plugins/lasso-writer-event-bundle-written/test.js ================================================ exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundlingEnabled: true, plugins: [ require('./plugin') ] }; }; exports.getLassoOptions = function() { return { dependencies: [ './browser.json' ] }; }; exports.check = function(lassoPageResult, writerTracker, helpers) { var events = require('./plugin').events; helpers.compare(helpers.normalizeOutput(events, { replaceVersions: true }), '-events.json'); }; ================================================ FILE: test/autotests/plugins/lasso-writer-event-resource-written/browser.json ================================================ { "dependencies": [ "./fonts/fonts.css" ] } ================================================ FILE: test/autotests/plugins/lasso-writer-event-resource-written/expected-events.json ================================================ [ { "url": "plugins-lasso-writer-event-resource-written/autotest$x.x.x/fonts/Aleo-Regular.woff", "outputFile": true, "sourceFile": true } ] ================================================ FILE: test/autotests/plugins/lasso-writer-event-resource-written/fonts/fonts.css ================================================ @font-face { src: url('Aleo-Regular.woff'); } ================================================ FILE: test/autotests/plugins/lasso-writer-event-resource-written/package.json ================================================ { "name": "autotest", "version": "0.0.0" } ================================================ FILE: test/autotests/plugins/lasso-writer-event-resource-written/plugin.js ================================================ 'use strict'; var events = []; module.exports = exports = function(lasso, config) { lasso.on('beforeBuildPage', (event) => { var context = event.context; context.writer.on('resourceWritten', event => { const correctSourceFile = event.sourceFile.endsWith('/test/autotests/plugins/lasso-writer-event-resource-written/fonts/Aleo-Regular.woff'); const correctOutputFile = event.outputFile.endsWith('/test/build/plugins-lasso-writer-event-resource-written/plugins-lasso-writer-event-resource-written/autotest$0.0.0/fonts/Aleo-Regular.woff'); events.push({ url: event.url, sourceFile: correctSourceFile, outputFile: correctOutputFile }); }); }); }; exports.events = events; ================================================ FILE: test/autotests/plugins/lasso-writer-event-resource-written/test.js ================================================ exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundlingEnabled: true, plugins: [ require('./plugin') ] }; }; exports.getLassoOptions = function() { return { dependencies: [ './browser.json' ] }; }; exports.check = function(lassoPageResult, writerTracker, helpers) { var events = require('./plugin').events; helpers.compare(helpers.normalizeOutput(events, { replaceVersions: true }), '-events.json'); }; ================================================ FILE: test/autotests/plugins/registerRequireExtension/package.json ================================================ { "name": "autotest", "version": "0.0.0" } ================================================ FILE: test/autotests/plugins/registerRequireExtension/plugin.js ================================================ module.exports = exports = function(lasso, config) { lasso.dependencies.registerRequireExtension( 'foo', { read: function(filename, lassoContext) { return 'module.exports="FOO"'; } }); }; ================================================ FILE: test/autotests/plugins/registerRequireExtension/something.foo ================================================ ================================================ FILE: test/autotests/plugins/registerRequireExtension/test.js ================================================ var expect = require('chai').expect; exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundlingEnabled: true, plugins: [ require('./plugin') ] }; }; exports.getLassoOptions = function() { return { dependencies: [ 'require: ./something.foo' ] }; }; exports.check = function(lassoPageResult, writerTracker) { var jsCode = writerTracker.getCodeForPath(lassoPageResult.getJavaScriptFiles()[0]); expect(jsCode).to.contain('module.exports="FOO"'); }; ================================================ FILE: test/autotests/plugins/registerRequireType/browser.json ================================================ { "dependencies": [ "./something.foo" ] } ================================================ FILE: test/autotests/plugins/registerRequireType/package.json ================================================ { "name": "autotest", "version": "0.0.0" } ================================================ FILE: test/autotests/plugins/registerRequireType/plugin.js ================================================ module.exports = exports = function(lasso, config) { lasso.dependencies.registerRequireType( 'foo', { properties: { 'path': 'string' }, async init (lassoContext) { if (!this.path) { throw new Error('"path" is required for a Marko dependency'); } this.path = this.resolvePath(this.path); }, object: true, // We are exporting a simple JavaScript object read (lassoContext) { return new Promise((resolve) => { setTimeout(function() { resolve(JSON.stringify({foo: 'bar'})); }); }); }, async getLastModified (lassoContext) { return lassoContext.getFileLastModified(this.path); } }); }; ================================================ FILE: test/autotests/plugins/registerRequireType/something.foo ================================================ ================================================ FILE: test/autotests/plugins/registerRequireType/test.js ================================================ var expect = require('chai').expect; exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundlingEnabled: true, plugins: [ require('./plugin') ] }; }; exports.getLassoOptions = function() { return { dependencies: [ './browser.json' ] }; }; exports.check = function(lassoPageResult, writerTracker) { var jsCode = writerTracker.getCodeForPath(lassoPageResult.getJavaScriptFiles()[0]); expect(jsCode).to.contain('$_mod.def("/autotest$0.0.0/something.foo", {"foo":"bar"})'); }; ================================================ FILE: test/autotests/plugins/write-bundle/browser.json ================================================ { "dependencies": [ { "type": "js", "path": "./foo.js" } ] } ================================================ FILE: test/autotests/plugins/write-bundle/foo.js ================================================ var a = 5; ================================================ FILE: test/autotests/plugins/write-bundle/package.json ================================================ { "name": "autotest", "version": "0.0.0" } ================================================ FILE: test/autotests/plugins/write-bundle/plugin.js ================================================ module.exports = function(lasso, config) { lasso.config.writer = { async init (lassoContext) { module.exports.counter.push('init'); await Promise.resolve(); }, async writeBundle (reader, lassoContext) { const bundle = lassoContext.bundle; bundle.url = 'test.com'; module.exports.counter.push('writeBundle'); }, async writeResource (reader, lassoContext) { module.exports.counter.push('writeResource'); return { url: 'test.com' }; } }; }; module.exports.counter = []; ================================================ FILE: test/autotests/plugins/write-bundle/test.js ================================================ var expect = require('chai').expect; const plugin = require('./plugin'); exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundlingEnabled: true, plugins: [plugin] }; }; exports.getLassoOptions = function() { return { dependencies: [ './browser.json' ] }; }; exports.check = function(lassoPageResult, writerTracker) { expect(plugin.counter).to.deep.equal(['init', 'writeBundle']); expect(lassoPageResult.getJavaScriptFiles().length).to.equal(0); expect(lassoPageResult.getJavaScriptUrls()).to.deep.equal(['test.com']); }; ================================================ FILE: test/autotests/prebuild-page/invalid-prebuild-config/test.js ================================================ const expect = require('chai').expect; exports.prebuildConfig = true; exports.checkError = function (err) { expect(err.message).to.equal('"pageConfig" should either be an array or object passed to "lasso.prebuildPage(...)"'); }; ================================================ FILE: test/autotests/prebuild-page/multi-prebuild/a.js ================================================ var a = true; ================================================ FILE: test/autotests/prebuild-page/multi-prebuild/b.js ================================================ var b = true; ================================================ FILE: test/autotests/prebuild-page/multi-prebuild/page.prebuild.expected.json ================================================ [ { "slots": { "body": [ { "contentType": "js", "content": [ { "inline": false, "code": { "src": "/static/page/lasso/test/autotests/prebuild-page/multi-prebuild/a.js" } } ] } ] }, "assets": [], "name": "page" } ] ================================================ FILE: test/autotests/prebuild-page/multi-prebuild/page1.prebuild.expected.json ================================================ [ { "slots": { "body": [ { "contentType": "js", "content": [ { "inline": false, "code": { "src": "/static/page1/lasso/test/autotests/prebuild-page/multi-prebuild/b.js" } } ] } ] }, "assets": [], "name": "page1" } ] ================================================ FILE: test/autotests/prebuild-page/multi-prebuild/test.js ================================================ const nodePath = require('path'); exports.prebuildConfig = [ { pageName: 'page', dependencies: [nodePath.join(__dirname, 'a.js')], pageDir: __dirname }, { pageName: 'page1', dependencies: [nodePath.join(__dirname, 'b.js')], pageDir: __dirname } ]; ================================================ FILE: test/autotests/prebuild-page/prebuild-resource/test-page.prebuild.expected.json ================================================ [ { "slots": { "body": [ { "contentType": "js", "content": [ { "inline": false, "code": { "src": "/static/test-page/lasso/src/index.js" } }, { "inline": false, "code": { "src": "/static/test-page/lasso/test/autotests/prebuild-page/prebuild-resource/ebay.png.js" } } ] }, { "contentType": "js", "content": [ { "inline": true, "code": "$_mod.ready();", "merge": true } ] } ] }, "assets": [ { "url": "/static/test-page/lasso/test/autotests/prebuild-page/prebuild-resource/ebay.png", "outputFile": "", "sourceFile": "" } ], "name": "test-page" } ] ================================================ FILE: test/autotests/prebuild-page/prebuild-resource/test.js ================================================ const nodePath = require('path'); exports.prebuildConfig = { pageName: 'test-page', dependencies: [ nodePath.join(__dirname, 'ebay.png') ], pageDir: __dirname }; ================================================ FILE: test/autotests/prebuild-page/valid-prebuild/a.js ================================================ var a = true; ================================================ FILE: test/autotests/prebuild-page/valid-prebuild/test-page-1.prebuild.expected.json ================================================ [ { "slots": { "body": [ { "contentType": "js", "content": [ { "inline": false, "code": { "src": "/static/test-page-1/lasso/test/autotests/prebuild-page/valid-prebuild/a.js" } } ] } ] }, "assets": [], "name": "test-page-1" } ] ================================================ FILE: test/autotests/prebuild-page/valid-prebuild/test.js ================================================ const nodePath = require('path'); exports.prebuildConfig = { pageName: 'test-page-1', dependencies: [nodePath.join(__dirname, 'a.js')], pageDir: __dirname }; ================================================ FILE: test/autotests/require-no-op/enable/test.js ================================================ 'use strict'; var expect = require('chai').expect; exports.check = function(nodeRequireNoOp) { nodeRequireNoOp.enable('.xfoo'); nodeRequireNoOp.enable('xbar'); nodeRequireNoOp.enable(['yfoo', '.ybar']); var test = require('./test.xfoo'); expect(Object.keys(test).length).to.equal(0); require('./test.xbar'); require('./test.yfoo'); require('./test.yfoo'); }; ================================================ FILE: test/autotests/require-no-op/enable/test.xbar ================================================ ================================================ FILE: test/autotests/require-no-op/enable/test.xfoo ================================================ ================================================ FILE: test/autotests/require-no-op/enable/test.ybar ================================================ ================================================ FILE: test/autotests/require-no-op/enable/test.yfoo ================================================ ================================================ FILE: test/autotests/require-no-op/enable-ignore-null-extension/test.js ================================================ 'use strict'; var expect = require('chai').expect; exports.check = function(nodeRequireNoOp) { nodeRequireNoOp.enable('.xfoo'); nodeRequireNoOp.enable('xbar'); nodeRequireNoOp.enable(null); nodeRequireNoOp.enable(['yfoo', null, '.ybar']); var test = require('./test.xfoo'); expect(Object.keys(test).length).to.equal(0); require('./test.xbar'); require('./test.yfoo'); require('./test.yfoo'); }; ================================================ FILE: test/autotests/require-no-op/enable-ignore-null-extension/test.xbar ================================================ ================================================ FILE: test/autotests/require-no-op/enable-ignore-null-extension/test.xfoo ================================================ ================================================ FILE: test/autotests/require-no-op/enable-ignore-null-extension/test.ybar ================================================ ================================================ FILE: test/autotests/require-no-op/enable-ignore-null-extension/test.yfoo ================================================ ================================================ FILE: test/autotests/require-no-op/enable-throw-invalid-extension-type/test.js ================================================ 'use strict'; var expect = require('chai').expect; exports.check = function(nodeRequireNoOp) { nodeRequireNoOp.enable(true); }; exports.checkError = function (e) { expect(e.message).to.equal('Expected extension to be a string. Actual: true'); }; ================================================ FILE: test/autotests/require-no-op/enable-throw-invalid-extension-type/test.xbar ================================================ ================================================ FILE: test/autotests/require-no-op/enable-throw-invalid-extension-type/test.xfoo ================================================ ================================================ FILE: test/autotests/require-no-op/enable-throw-invalid-extension-type/test.ybar ================================================ ================================================ FILE: test/autotests/require-no-op/enable-throw-invalid-extension-type/test.yfoo ================================================ ================================================ FILE: test/autotests/resource-transforms/filter/package.json ================================================ { "name": "autotest", "version": "0.0.0" } ================================================ FILE: test/autotests/resource-transforms/filter/test.js ================================================ var expect = require('chai').expect; var nodePath = require('path'); var fs = require('fs'); exports.getLassoConfig = function() { return { fingerprintsEnabled: true, bundlingEnabled: true, plugins: [ function myPlugin(myLasso, pluginConfig) { myLasso.addTransform({ async filter (lassoContext) { var path = lassoContext.path; if (!path) return false; return /\.bar$/.test(path); }, name: 'testTransformer', stream: true, transform: function(stream, contentType, lassoContext, callback) { var through = require('through'); return stream.pipe(through( function write(chunk) { this.push(chunk); }, function end(chunk) { this.push(new Buffer('-TRANSFORMED', 'utf8')); this.push(null); } )); } }); } ] }; }; exports.getLassoOptions = function() { return {}; }; exports.getInputs = function() { return [ { path: nodePath.join(__dirname, 'transform.bar'), options: {}, check(result) { var outputFile = result.outputFile; expect(fs.readFileSync(outputFile, {encoding: 'utf8'})).to.equal('hello-TRANSFORMED'); } }, { path: nodePath.join(__dirname, 'transform.foo'), options: {}, check(result) { var outputFile = result.outputFile; expect(fs.readFileSync(outputFile, {encoding: 'utf8'})).to.equal('world'); } }, ]; }; ================================================ FILE: test/autotests/resource-transforms/filter/transform.bar ================================================ hello ================================================ FILE: test/autotests/resource-transforms/filter/transform.foo ================================================ world ================================================ FILE: test/autotests/resource-transforms/stream/package.json ================================================ { "name": "autotest", "version": "0.0.0" } ================================================ FILE: test/autotests/resource-transforms/stream/test.js ================================================ var expect = require('chai').expect; var nodePath = require('path'); var fs = require('fs'); exports.getLassoConfig = function() { return { fingerprintsEnabled: true, bundlingEnabled: true, plugins: [ function myPlugin(myLasso, pluginConfig) { myLasso.addTransform({ contentType: ['foo', 'bar'], name: 'testTransformer', stream: true, transform: function(stream, lassoContext, callback) { var contentType = lassoContext.contentType; var deferredStream = lassoContext.deferredStream(function() { var chunks = []; stream .on('data', function(chunk) { chunks.push(chunk); }) .on('error', function(err) { return callback(err); }) .on('end', function() { var buffer = Buffer.concat(chunks); // Create a buffer from all the received chunks var str = buffer.toString('utf8'); // results[lassoContext.path] = str; if (contentType === 'foo') { deferredStream.push(str + '-FOO'); } else if (contentType === 'bar') { deferredStream.push(str + '-BAR'); } else { throw new Error('Unexpected content type: ', contentType); } deferredStream.push(null); }); }); return deferredStream; } }); } ] }; }; exports.getLassoOptions = function() { return {}; }; exports.getInputs = function() { return [ { path: nodePath.join(__dirname, 'transform.bar'), options: {}, check(result) { var outputFile = result.outputFile; expect(fs.readFileSync(outputFile, {encoding: 'utf8'})).to.equal('hello-BAR'); } }, { path: nodePath.join(__dirname, 'transform.foo'), options: {}, check(result) { var outputFile = result.outputFile; expect(fs.readFileSync(outputFile, {encoding: 'utf8'})).to.equal('world-FOO'); } }, ]; }; ================================================ FILE: test/autotests/resource-transforms/stream/transform.bar ================================================ hello ================================================ FILE: test/autotests/resource-transforms/stream/transform.foo ================================================ world ================================================ FILE: test/autotests/transforms/minify-js/browser.json ================================================ { "dependencies": [ "./foo.js" ] } ================================================ FILE: test/autotests/transforms/minify-js/foo.js ================================================ (function(console) { var name = 'John' + ' ' + 'Doe'; function hello(name) { console.log(name); } hello(name); }(console)); ================================================ FILE: test/autotests/transforms/minify-js/package.json ================================================ { "name": "autotest", "version": "0.0.0" } ================================================ FILE: test/autotests/transforms/minify-js/test.js ================================================ var expect = require('chai').expect; exports.getLassoConfig = function() { return { fingerprintsEnabled: true, bundlingEnabled: true, minifyJS: true }; }; exports.getLassoOptions = function() { return { dependencies: [ './browser.json' ] }; }; exports.check = function(lassoPageResult, writerTracker) { var jsCode = writerTracker.getCodeForPath(lassoPageResult.getJavaScriptFiles()[0]); expect(jsCode).to.not.contain('hello'); expect(jsCode).to.not.contain('name'); expect(jsCode).to.contain('console'); }; ================================================ FILE: test/autotests/transforms/output-transforms/browser.json ================================================ { "dependencies": [ "transformsA.js", "transformsA.css" ] } ================================================ FILE: test/autotests/transforms/output-transforms/css-transform1.js ================================================ var through = require('through'); exports.stream = true; exports.transform = function(inStream, context) { var contentType = context.contentType; if (contentType === 'css') { return inStream.pipe(through(null, function end () { //optional this.queue('-CSSTransform1'); this.queue(null); })); } else { return inStream; } }; exports.name = module.id; ================================================ FILE: test/autotests/transforms/output-transforms/css-transform2.js ================================================ exports.stream = false; exports.transform = function(code, context) { var contentType = context.contentType; if (contentType === 'css') { return code.toUpperCase() + '-CSSTransform2'; } else { return code; } }; exports.name = module.id; ================================================ FILE: test/autotests/transforms/output-transforms/js-transform1-async.js ================================================ var through = require('through'); exports.stream = true; exports.transform = function(inStream, context) { var contentType = context.contentType; if (!inStream) { throw new Error('inStream expected'); } if (contentType === 'js') { var code = ''; return inStream.pipe(through(function write(data) { code += data; }, function end () { setTimeout(function() { this.queue(code + '-JavaScriptTransform1Async'); this.queue(null); }.bind(this), 100); })); } else { return inStream; } }; exports.name = module.id; ================================================ FILE: test/autotests/transforms/output-transforms/js-transform2-async.js ================================================ 'use strict'; var ok = require('assert').ok; exports.stream = false; exports.contentType = 'js'; exports.transform = function(code, context) { var contentType = context.contentType; ok(contentType === 'js', '"js" content type expected'); return new Promise((resolve, reject) => { setTimeout(function() { resolve(code + '-JavaScriptTransform2Async'); }, 100); }); }; exports.name = module.id; ================================================ FILE: test/autotests/transforms/output-transforms/package.json ================================================ { "name": "autotest", "version": "0.0.0" } ================================================ FILE: test/autotests/transforms/output-transforms/test.js ================================================ var expect = require('chai').expect; exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundlingEnabled: true, plugins: [ { plugin: function(theLasso, config) { theLasso.addTransform(require('./css-transform1.js')); theLasso.addTransform(require('./css-transform2.js')); theLasso.addTransform(require('./js-transform1-async.js')); theLasso.addTransform(require('./js-transform2-async.js')); } } ] }; }; exports.getLassoOptions = function() { return { dependencies: [ './browser.json' ] }; }; exports.check = function(lassoPageResult, writerTracker) { expect(writerTracker.getOutputFilenames()).to.deep.equal( [ 'transforms-output-transforms.css', 'transforms-output-transforms.js' ] ); expect(writerTracker.getCodeForFilename('transforms-output-transforms.js')).to.equal('transformsA_js-JavaScriptTransform1Async-JavaScriptTransform2Async'); expect(writerTracker.getCodeForFilename('transforms-output-transforms.css')).to.equal('TRANSFORMSA_CSS-CSSTRANSFORM1-CSSTransform2'); }; ================================================ FILE: test/autotests/transforms/output-transforms/transformsA.css ================================================ transformsA_css ================================================ FILE: test/autotests/transforms/output-transforms/transformsA.js ================================================ transformsA_js ================================================ FILE: test/autotests/transforms/resolve-font-urls/browser.json ================================================ { "dependencies": [ "./fonts/fonts.css" ] } ================================================ FILE: test/autotests/transforms/resolve-font-urls/fonts/fonts.css ================================================ @font-face { src: url('Aleo-Regular.woff'); } ================================================ FILE: test/autotests/transforms/resolve-font-urls/package.json ================================================ { "name": "autotest", "version": "0.0.0" } ================================================ FILE: test/autotests/transforms/resolve-font-urls/test.js ================================================ var expect = require('chai').expect; exports.getLassoConfig = function() { return { fingerprintsEnabled: true, bundlingEnabled: true }; }; exports.getLassoOptions = function() { return { dependencies: [ './browser.json' ] }; }; exports.check = function(lassoPageResult, writerTracker) { var expected = "@font-face { src: url('Aleo-Regular-6be64eb6.woff'); }"; var actual = writerTracker.getCodeForPath(lassoPageResult.getCSSFiles()[0]); // console.log(actual); expect(actual).to.equal(expected); }; ================================================ FILE: test/autotests/transforms/transform-css-urls-custom-resolver/browser.json ================================================ { "dependencies": [ "./foo.css" ] } ================================================ FILE: test/autotests/transforms/transform-css-urls-custom-resolver/foo.css ================================================ .test-1 {background-image: url(SOME_DIR/MYCOMPANY-logo.png);} .test-2 {background-image: url("SOME_DIR/MYCOMPANY-logo.png");} .test-3 {background-image: url('SOME_DIR/MYCOMPANY-logo.png');} .test-4 {background-image: url('SOME_DIR/MYCOMPANY-logo.png?base64');} ================================================ FILE: test/autotests/transforms/transform-css-urls-custom-resolver/package.json ================================================ { "name": "autotest", "version": "0.0.0" } ================================================ FILE: test/autotests/transforms/transform-css-urls-custom-resolver/test.js ================================================ var expect = require('chai').expect; exports.getLassoConfig = function() { return { fingerprintsEnabled: true, bundlingEnabled: true, resolveCssUrls: { async urlResolver (url, lassoContext) { url = url.replace('SOME_DIR', __dirname); url = url.replace('MYCOMPANY', 'ebay'); return url; } } }; }; exports.getLassoOptions = function() { return { dependencies: [ './browser.json' ] }; }; exports.check = function(lassoPageResult, writerTracker) { var expected = ".test-1 {background-image: url('ebay-logo-d481eb85.png');}\n.test-2 {background-image: url('ebay-logo-d481eb85.png');}\n.test-3 {background-image: url('ebay-logo-d481eb85.png');}\n.test-4 {background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAE8AAAAeCAYAAABt5kPUAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDpGNzdGMTE3NDA3MjA2ODExODhDNkIzODA1MTg5Nzc0NiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpGRTg2MEMyMzZFNzIxMUUyQjZGMEE2ODM0MDRENkNFNiIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpGRTg2MEMyMjZFNzIxMUUyQjZGMEE2ODM0MDRENkNFNiIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IE1hY2ludG9zaCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjAyODAxMTc0MDcyMDY4MTE5OTRDOTI2RkUxMEEyMEJFIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkY3N0YxMTc0MDcyMDY4MTE4OEM2QjM4MDUxODk3NzQ2Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+Yff30gAAE/BJREFUeNrsWgdwHdW5/s+Wu7u3q3fJspqLLMuVYINt3AgQwjPFYCCN+ogpLyRgCKQHAo/H8OAFhgAOxQQCDt3E2Bg74IK7ZMuyZCFbVpescnvZet5/9kpuhJbMvJkwb2d2tPferd/5/6+cFTHDESCiAPrBZjD7+oBqOpjHjoE5MACEFwAoBbZwGekgDh2D95QJ8HthGsjUAAKpheCGYYJ3b0fibwmd5nIERPx6A67L4KRF1SlU5jhgdrkTMj0C9IcN0A0K00oUEHkCPicP51S5QBIIuPg48Byet3OVfQ/UiADoIaBmDAjnAOCdQNVjAIIXvx8CIuUCWDoQORe2awPQFjsEeDjgkXifBM4uuwPSXaVg4j5w/L45SOpBSGgBe3t04QgPCT0AezqfxedS7d84wYLAUR+0bckHt1eG1tZWEOArLCrl4JtGK+wX8+CteC64iXESeJQLxoxCVaNZI6hmn3KwScGp8AicC9wyB5YF//IL91V2ZjVocRzc7DkM1WQINBwhrLKRFcf3xIdTV1zcLgEunOKzgUOg4euwcF/1AB148BlxuNnVCg5sNevL4IBVdn6NB4rSBLtNvy7LVwaPYP0lsH2neeKwNHMYt/kvQJvCTOS4ylwJ1K8RcJ8LHmFExiMwAg9EEGxRIewzx9sUrJkAV+aFoMRpgGaRv38SBKsgU4SFEzw2xzEBEFEMHCMrjy1NyAlK+OKRI6lbJkjVTDQ4MbWyz0ghx0/2f7R8SjCIQ0SgRFQyLY1Go8X4Nx2o5eKiUdOKxUK839/NeT1dpiiZaQ4KF/sG4L9juSAR+imCJNjWy85IA7+Tg56g4R2ImGM1w8hCBXQqDi6uGdCriOSw7OASo6r+aaKlKWA4CewNI1xI9WGUVuLHvwrwLg3MRBDB7CO8sxN4WTeZvAKhKQVlQ41qyQmfw+T0lOGzFRovRXBQCIoi27ZoAp/QoDwWkMCKCQdKOH48qyoETW9sWpDcum252d19NtqWDGR3QmlKGklqnyTn9x+QamueETPTn7+owpVcbwpwsFc9rV0tmD/JA1V5jsJnPgqsaOxJXhLTrDzKxILa/Y9FTCyXxB0NJczn51a6/ifTLQTo6Q+GFYY2xWsNbbneCuy8mmoDVWhJFJtsT/ZKRFSJI6NFSJ/1TH76zNf3q70PmVogEwE0TEuTA/G236Q7x240QT+NhpjoiadYFQEHKqAdre0N1f8nY2zTUuUxOdO3e8xpdx1q2XpOfkEur6pqXLAvLOENqmp69NkXntEbDy6hoz4Cb9BuVyHl96iOF1ZV2Uwkpse7u6dr9ftvybnthisvO6N6/89f6z4xdrjh9QrJbJ940YrVfc8OB/Q0+3uejN6xbV3w9FzENMe+sSP4q43N0RtvW5h5zaKJ7nW2qLCH4SW8r77pRusjq6kaGPOpFmYrq0xbtTSJJnon6V2vPTo23PCDAjmD1qv9UySsGt2MQ33ni9rYjHkbUxenI0VtIX24kUpcp5zagR7yo8MP3RWMty0SeRceH4N8z4wXnJXzoHn80ODWbVtkrL4wsQJBMEMhX+Dun72t7ambQ1xoPjUNhMKCFqGs7GUEb5tjSu2QfviIYg0PT7IGBy/RDjYvYFXI9nNkZ/UIv71vwXVNBe1v7RpuN030ebh4ndwAMUEIhfU0t1ccnlysvJbvEz6qLZL7mvpUX+ewPqOpV7302LBeBiKxhUUUifb0NYWXXzHD9yanDwKvduWpO5ZspdGOUpswcVCJq2Q3cZWu4pxj6jhvTcIc3OgDU51mRVuW0Wh7LdtPwP2GRG/yRYciayMiZ6GpXzL5qXNLM+atZ2B+Jo9xMgxEmya/smfZdjTUskl1yPZM+Pjs8jvPzvFXmI07huGee39qA8/fe8stEHniD/+V2PDBZZzXyyoQHNOmrnZf+/3zxNKS98E0Dztqa3rx+w4hP2+3cu6iFzi3O6w3NZ/LKtIMBDyewe6K3qlz31zXoi3Hs7rZ4GKacKkJUynOk3f++zkZi8+ucK3yO/n9U4udR3DcmypypA2LJ3pWRlSrpKNfq2EAWjrlt7bE586f6FudLx4JmS0P3mn2b7uIUR4THy5nzjpH5Z0LEIuPiZTdQfy1PZDsbeP8tVuFwiuepdqxGTR6tNxCAH1WUohzkt7O8RxrL2w9TA3D+eNyL3xhlIb+3sIjTXzY+sDDvaG6qQJWPgN9TvmdNxf6pzezM7W19sLmzZtTaqs1NFYmNmz8AQICNJkER82knRmPP/odPj09ru1rAO1gEyTWbwBtbx1oBxrBCobAe+vyR5Tzzn2c7U9cLlD31J87vuXjC0GW4ycnivICueW15SXfqi6Q25DX0KpYkMSVWZaoaoFX5mLPXVN49Xm1njcAPzMAB4J63gNrB++JJC0k5LjKp1Vu5HzVa7i0ce/zuRf8EiE0wIiihiUAwo0AiS6wjq3HLhjUhMJl9yJ3W/gjwxqmyAUNHimrgwEg8Ap0BrYvwHW+iNufVXX9kQO1nxxbt1TE1tXNBBT6Z3w8IW/JGsWRjgY/KxUERvfXdu5aQqMxF3GmTijPnfP75IYP1MjK58Boa7cTgrpjFx7EYVFRUHfuAv3wYRDHj3uJvLeeVRrmRQoZ+3dcpmRNg8RxY0zhe7PTHszyCAPsuGDcBAbIyE+giBxU5TrAg4njpnMyfvdeY/TbuBsPDgKbDob+7f0ZY+89P/PS+xR/x32WHZWRf5F/rPABHBjMtyQTL60hThpYoYNgDt0OfP6SZiL6u6g2VIyKC+l6ODAlZ+Erm3vfeNCB/Ghgrt3V/sxdRf4zNhK7mSmcbJSYaNR1rrpNM6KiA3mQKeq04mt/x/MOSzNi6PXxGKIfNwYCVtNZzMuxbziP29T27e9Wt32cSw1DIpJ0mjLhbtE4oLAYjqlT41h1QbQzfosXwNXdNlP2xuQE81yWCR6vECzPcaxr6VNt0PP9AgxFDfvCLPhne3j4pF+DZvzdwXO7xuVKO5u6kmcC/hZKGNl7u6CqWJ408A0fHq8nEbQGVOpENVUHS1BxMyDe5iGcJNF4h4Atg14iScyul7FDOR+MKKcRbfdMLsp9qsFddUMk2lzGKq59ePOio8ObzyvLXLBWM6NYG4JtRQRehp5Q3fRD/e9cxarOwDbP89a+Lwm+dw4f22QDLAgcODNjqK8OSKpJ5NbBoRLbgiDKaE245JZtbxPkjNOBO1Gr6H8wVej19bgtKDBSkWI8ksMlse54rx3H0l384e5ho7tjULfH1Y65IyUv4uWC2MbAVkhVYZZX2NdE4czROHegWy25eryxBWLN843eD25HQZgDZtJzWrGMToMAkE/7fWQOwR0+GKzJv/jRD5t/+RjjM8Z39V0v3jomY+5aVg4EeUzkFFtx93W9dKuKVSehAhNMUTUFVzzmU/Lt9h0V+IpyHmbPmwLr1mwBASvHBmBEu7FyOdfxz5+d0Rhwp7olQwdiIhj8KPGScAK9nn7yJAAe1xXQAVsZtJOimtPBQbqTD6T6gdkPAsMx3Tk29oefq+1//JU1ek1r5C8vonMVYmhikyB6NKqHkAdjVsoPc0VsfEbzkxn4GCZWrXiyxT/tmmPhfbWs+tqGPvxm2+CmC8qyFr5rUbQEKAy9wb21h/rXLHXY1iQBZZkL/zox7+I16BGBiifuVUJbt2zZ5bBx/XbkZIdDBz1lHLHawvj5t9bQUBy3PxtBtALMpoyOPmdpZm9eqTshuX6GhOYe2cub7xdx1xSzMKfBRKJjWMOWoJDm5NkcYIpnUSj6wkba8XhFnLAs99Wlzr4/nh0nI8gRQUe1XcUpeW8CpxzBXh6kWiApjLnORB9omb0bDS5nlouGG/cgmKV2JbKKTPaBUwvoNUXfffj9Az9aZRsXrL69nc/9pDRj7rus8lgC2X70iTtVPSyxCuQw8s0svfEhh+CB020NRTItr6iCpVdcjKCnp/fp4fB4e+IT/aHr0ovfULfvaNX21iOYLD+eiqGtsE4nyGfNspWWR36LUgEeS7/UHx9QfgJJzc1GfDBilDkEKPA7xW5qiwpAY08Mwkn0+Aja7HI3ZLl5G0gBzfPLO0O1x8uT06DaemU+VhpnA8r2qfzxT/jsxY9ZWEkU1Ra0EO4axlGRsc58wJdeBXzmvDS98U7fyaVOmBkf2gIVZTf/ubFz1Q97w/Vn2sob3DHv6PCWxRPzlqzvDu2deqj/3UsZ1zFLk++buhZv+W+HBzb+3dQtKxJUTMHHFErHbAfDsEfJCoVkfJJ5/t/8AlxXXm6DZKcLBiDyIsu80lmzIf2Rh8B3z12K6/JLs5Urr8jK/84l6QsrHB6qG9YoB4VDhr9r2Fg8s1SBkgwHxrcktqJpT2PFNQvahzS7GnO8AnQP69Mbu5MzmViAxUG2Mx6r8ParujWSbUVnmAiu1VaojqkA7sOsr4nFhRkcK0souQ7Eqnvwt9gMTCLpp0wQsKId/IixiTGl6Hv3s4FkSmtZBuzuWLnCwHPt6Vh5i45cx47jMarVFl51v4IDIglsVtv9qZXqIkwYPw4E9Gt/SW7ecgeeTWA8lvjrez9SFi98XT5nzjBRZMBUgUoXAQ6B5HJyQCwvA0wcELrvgUcSH2y6lshy1GkklLHfvvEmIs2K0fiIWUGQVm4ZvnvJNO9a5Lm+uEZtsEbFg3FhIG5BMQL7/LbAvZaKqDGmUAksnKy+WuiNz0xEYWKKJkwReJeT2DeO1BjvRChCwPkm29aFc5Xj9iQwelb/R6pQToBHWfeGG0CPtuC15qwpSjtjE3q9c1iVoRGeu+7gil8f6l97EWtX1qLlmQvfrcg+d4thal8418NJs76xV543509WDL0TSrDRdnRC8J5frNYPNueyxMH5fMD5Uf3xL5+eBlYgAJHHn/xx4r31N2LORbUe9Atez5G6jImbaFI/4T4RvNauZMVFj7W/VdeRqHAjMCKLTogcW9lnNM6+657r+tO6fZGLbOAwomVnCh13XFByN5WK2+0gwLjLUBUabrgpVUb8iGey7EkDrEisvm5Z23/bE9bg1gWnTLLRlMiQeBD4YB3wgheml1z/a7QlNJUyKL+v+6W7DSuZxvbFTGvNLv/Rg24pB1xSJq5Zn7sKrBUxMazQWw9PNj5prSWKwpLEfLO7p06orHiK83o2oucLkFhc1CKRKr3x4PfNox2LRr0h2hxLWr78HiWtqp/bfVS2Rm4ao1iPyhPlUEdi5v0BbXd1ofIiKuo6t0wGseLcb+8Lz97fmfx+36BWDA7Ojl+EJ9oDl2XfVlsm9Rv8dU/D4A/Pt0HCgTDan/sxCews5tJmPAlGpAfMBKWJbg8mi5lWYPdNVrS3msi+IeLwBdH7laU8kQc47yTkvThIaKxVzKklaWf+DfPt6y3H1l7ClJWzyZ6AbiUBBWS1yLs3dwb2fKkZRsLenrGpKHXrtqzIUyuf1ZsPXWDPXI7MWNicx6eAYqpMR+01iwMZ6QMZV19+7Uc157/z5H4oXLNruCmpodoiwZfmSn+cN9H90ut7wn8OBfXMlEcceafBTmGOXGPEgvi8Qt/tizKv/u6stA+KfCZylAZq3fUrzK5XH7AtCjfyPMSeyzLYfB0qh8jMHLslTvaHUHkvNAO7LrH6PrzNLlJXQZNQcftk1FfdQC8XyJiFY+5EZa8/Y03DrVuw+gSWKmw/QC26cNx9Z2W4yrYxg/xlFv4XP2VEa2BmDcQdlZV/5vNyt9KkKuN32QiCk1oW5jNttMrwJuUEn59XL02pfdh54bduFGpq6o+ADyxBcvVH9HEZXqHN7xU6a4uV9zH4r67MkVa5nHw8rNOCuE6x72nqxQdWE1YazfGLrVNLlUeWzvBfO61EafTIAmS6MO1YMQQ4uZXzVmNwVbOQ6/LRJ0g2UtRiUyw8a2kipfVwaZOfEUpv/C7nGddAE+05hFg+omS3ECXvIO+b/Fe8aZNwPJjOQjbvh+ev7B6OH5nUH2mYyIyzjtGrKveCl2eW3PAo84GKmPalVmJGorZvw1gGNBwGCwkfjTOYwaDfGhgqtwYH8oRJE71G6xGNc7kGkAPbhKKCTmqYFidjCikqhsi4GoiYPLy2K2i/GTPxASuyJZYaYPfROHgVHi2KJaPCVh05po0tzXK4WvrVWJpTaKstkptVgyaZ10OgobpAgaosC3gzDEbXy1itHlsArODePJroGg+cmA1ShkCDdRHOXdVNlKJm4J1RgsEdI1rKxjBetNsdRc4/PfUeFo1wCIHiMY4l9EDWOw3LtwfjHWM5HACek5JXzXz9zBxPdT3y3z84DT/amriiSQ5ymRm7mUTKs84ElVULjh77TJnxRZNMUWBYNzGHktDZew2aAg9/163UjDHrziQKARZassAn7sMQsm9epdt+Ac7eZ3D29JXF8u1nGPLUjBwqbS8o+b1ELsB2LEXK60HDlZt6l8HaDJNCamJUZ2Q0IijoPpDnVAQohsIi4N0yq1HX9cItg9GWsRIOjIZcODZrwes+pag+ovb9c+8wTk4RwOIWtjQzxsga+AA09bIFvvpbMHtcRkBlsY2BzOyjRU9xFp+zMED0FFAmVgdF2rOYP/3suTmCALJfVTkHWIUJeO+RZG/BgZ6/3MDakymuJHgTZZnzH+gO7jnlvwn+OfD+xReClchmS2JoOZIINMdeIyBn7+p4+o6o2pfDlFbDFp+Uu/T5MRlzGnQj/s+/PfvXX5jYYKuKXoh6qzGwOFJA4t/B6CcTDvWtuZ4nDrvKZNGfQN/3hAd9nSGq/w8e4wDVX4vrNPtfQHibA3lgbdrU9+aVlqWbiujvxjThrs6/ZGWOt7qBTXT+I8v/CjAAJOOJ0FaTyhsAAAAASUVORK5CYII=');}"; var actual = writerTracker.getCodeForPath(lassoPageResult.getCSSFiles()[0]); // console.log(actual); expect(actual).to.equal(expected); }; ================================================ FILE: test/autotests/transforms/transform-css-urls-custom-type/browser.json ================================================ { "dependencies": [ "./foo.css" ] } ================================================ FILE: test/autotests/transforms/transform-css-urls-custom-type/foo.css ================================================ .foo {background-image: url(ebay-logo.png);} ================================================ FILE: test/autotests/transforms/transform-css-urls-custom-type/package.json ================================================ { "name": "autotest", "version": "0.0.0" } ================================================ FILE: test/autotests/transforms/transform-css-urls-custom-type/test.js ================================================ const fs = require('fs'); const expect = require('chai').expect; const path = require('path'); exports.getLassoConfig = function() { return { fingerprintsEnabled: true, urlPrefix: '/static', bundlingEnabled: true, plugins: [ { plugin: function(theLasso) { theLasso.dependencies.registerStyleSheetType('foo', { properties: { 'path': 'string' }, async init (lassoContext) { this.path = this.resolvePath(this.path); }, async read (lassoContext) { const css = await fs.promises.readFile(this.path, 'utf8'); return css; }, getSourceFile: function() { return this.path; }, async getLastModified (lassoContext) { return -1; } }); } } ] }; }; exports.getLassoOptions = function() { return { dependencies: [ { type: 'foo', path: path.join(__dirname, 'foo.css') } ] }; }; exports.check = function(lassoPageResult, writerTracker) { var expected = '.foo {background-image: url(\'ebay-logo-d481eb85.png\');}'; var actual = writerTracker.getCodeForPath(lassoPageResult.getCSSFiles()[0]); // console.log(actual); expect(actual).to.equal(expected); }; ================================================ FILE: test/autotests/transforms/transform-css-urls-fingerprints/browser.json ================================================ { "dependencies": [ "./foo.css" ] } ================================================ FILE: test/autotests/transforms/transform-css-urls-fingerprints/foo.css ================================================ .foo { url-relative:url(ebay.png); url-dot-relative:url(./ebay.png); installed:url(installed/ebay.png); installed-require-prefix:url(require: installed/ebay.png); installed-require-prefix:url('http://foo.void/ebay.png'); absolute:url(/ebay.png); } ================================================ FILE: test/autotests/transforms/transform-css-urls-fingerprints/package.json ================================================ { "name": "autotest", "version": "0.0.0" } ================================================ FILE: test/autotests/transforms/transform-css-urls-fingerprints/test.js ================================================ var expect = require('chai').expect; exports.getLassoConfig = function() { return { fingerprintsEnabled: true, bundlingEnabled: true }; }; exports.getLassoOptions = function() { return { dependencies: [ './browser.json' ] }; }; exports.check = function(lassoPageResult, writerTracker) { var cssCode = writerTracker.getCodeForPath(lassoPageResult.getCSSFiles()[0]); expect(cssCode).to.contain('url-relative:url(\'ebay-1cace448.png\')'); expect(cssCode).to.contain('installed:url(\'ebay-1cace448.png\')'); expect(cssCode).to.contain('installed-require-prefix:url(\'ebay-1cace448.png\')'); expect(cssCode).to.contain('installed-require-prefix:url(\'http://foo.void/ebay.png\')'); expect(cssCode).to.contain('absolute:url(\'ebay-1cace448.png\')'); }; ================================================ FILE: test/autotests/transforms/transform-css-urls-no-bundling/browser.json ================================================ { "dependencies": [ "./foo.css" ] } ================================================ FILE: test/autotests/transforms/transform-css-urls-no-bundling/foo.css ================================================ .foo { url-relative:url(ebay.png); url-dot-relative:url(./ebay.png); installed:url(installed/ebay.png); installed-require-prefix:url(require: installed/ebay.png); installed-require-prefix:url('http://foo.void/ebay.png'); absolute:url(/ebay.png); } ================================================ FILE: test/autotests/transforms/transform-css-urls-no-bundling/package.json ================================================ { "name": "autotest", "version": "0.0.0" } ================================================ FILE: test/autotests/transforms/transform-css-urls-no-bundling/test.js ================================================ var expect = require('chai').expect; exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundlingEnabled: false }; }; exports.getLassoOptions = function() { return { dependencies: [ './browser.json' ] }; }; exports.check = function(lassoPageResult, writerTracker) { var cssCode = writerTracker.getCodeForPath(lassoPageResult.getCSSFiles()[0]); expect(cssCode).to.contain('url-relative:url(\'ebay.png\')'); expect(cssCode).to.contain('url-dot-relative:url(\'ebay.png\')'); expect(cssCode).to.contain('installed:url(\'../installed$1.0.0/ebay.png\')'); expect(cssCode).to.contain('installed-require-prefix:url(\'../installed$1.0.0/ebay.png\')'); expect(cssCode).to.contain('installed-require-prefix:url(\'http://foo.void/ebay.png\')'); expect(cssCode).to.contain('absolute:url(\'ebay.png\')'); }; ================================================ FILE: test/autotests/transforms/transform-css-urls-no-fingerprints/browser.json ================================================ { "dependencies": [ "./foo.css" ] } ================================================ FILE: test/autotests/transforms/transform-css-urls-no-fingerprints/foo.css ================================================ .foo { url-relative:url(ebay.png); url-dot-relative:url(./ebay.png); installed:url(installed/ebay.png); installed-require-prefix:url(require: installed/ebay.png); installed-require-prefix:url('http://foo.void/ebay.png'); absolute:url(/ebay.png); } ================================================ FILE: test/autotests/transforms/transform-css-urls-no-fingerprints/package.json ================================================ { "name": "autotest", "version": "0.0.0" } ================================================ FILE: test/autotests/transforms/transform-css-urls-no-fingerprints/test.js ================================================ var expect = require('chai').expect; exports.getLassoConfig = function() { return { fingerprintsEnabled: false, bundlingEnabled: true }; }; exports.getLassoOptions = function() { return { dependencies: [ './browser.json' ] }; }; exports.check = function(lassoPageResult, writerTracker) { var cssCode = writerTracker.getCodeForPath(lassoPageResult.getCSSFiles()[0]); expect(cssCode).to.contain('url-relative:url(\'transforms-transform-css-urls-no-fingerprints/autotest$0.0.0/ebay.png\')'); expect(cssCode).to.contain('url-dot-relative:url(\'transforms-transform-css-urls-no-fingerprints/autotest$0.0.0/ebay.png\')'); expect(cssCode).to.contain('installed:url(\'transforms-transform-css-urls-no-fingerprints/installed$1.0.0/ebay.png\')'); expect(cssCode).to.contain('installed-require-prefix:url(\'transforms-transform-css-urls-no-fingerprints/installed$1.0.0/ebay.png\')'); expect(cssCode).to.contain('installed-require-prefix:url(\'http://foo.void/ebay.png\')'); expect(cssCode).to.contain('absolute:url(\'transforms-transform-css-urls-no-fingerprints/autotest$0.0.0/ebay.png\')'); }; ================================================ FILE: test/autotests/transforms/transform-css-urls-no-relative/browser.json ================================================ { "dependencies": [ "./foo.css" ] } ================================================ FILE: test/autotests/transforms/transform-css-urls-no-relative/foo.css ================================================ .foo { url-relative:url(ebay.png); url-dot-relative:url(./ebay.png); installed:url(installed/ebay.png); installed-require-prefix:url(require: installed/ebay.png); installed-require-prefix:url('http://foo.void/ebay.png'); absolute:url(/ebay.png); } ================================================ FILE: test/autotests/transforms/transform-css-urls-no-relative/package.json ================================================ { "name": "autotest", "version": "0.0.0" } ================================================ FILE: test/autotests/transforms/transform-css-urls-no-relative/test.js ================================================ var expect = require('chai').expect; exports.getLassoConfig = function() { return { fingerprintsEnabled: true, bundlingEnabled: true, urlPrefix: 'https://cdn.example.net/build', relativeUrlsEnabled: false }; }; exports.getLassoOptions = function() { return { dependencies: [ './browser.json' ] }; }; exports.check = function(lassoPageResult, writerTracker) { var cssCode = writerTracker.getCodeForPath(lassoPageResult.getCSSFiles()[0]); expect(cssCode).to.contain('url-relative:url(\'https://cdn.example.net/build/ebay-1cace448.png\')'); expect(cssCode).to.contain('installed:url(\'https://cdn.example.net/build/ebay-1cace448.png\')'); expect(cssCode).to.contain('installed-require-prefix:url(\'https://cdn.example.net/build/ebay-1cace448.png\')'); expect(cssCode).to.contain('installed-require-prefix:url(\'http://foo.void/ebay.png\')'); expect(cssCode).to.contain('absolute:url(\'https://cdn.example.net/build/ebay-1cace448.png\')'); }; ================================================ FILE: test/autotests/transforms/transform-css-urls-require/browser.json ================================================ { "dependencies": [ "./foo.css" ] } ================================================ FILE: test/autotests/transforms/transform-css-urls-require/foo.css ================================================ .test-1 {background-image: url(require:assets/ebay-logo.png);} .test-2 {background-image: url("require:assets/ebay-logo.png");} .test-3 {background-image: url('require:assets/ebay-logo.png');} .test-4 {background-image: url('require:assets/ebay-logo.png?base64');} ================================================ FILE: test/autotests/transforms/transform-css-urls-require/package.json ================================================ { "name": "autotest", "version": "0.0.0" } ================================================ FILE: test/autotests/transforms/transform-css-urls-require/test.js ================================================ var expect = require('chai').expect; exports.getLassoConfig = function() { return { fingerprintsEnabled: true, bundlingEnabled: true }; }; exports.getLassoOptions = function() { return { dependencies: [ './browser.json' ] }; }; exports.check = function(lassoPageResult, writerTracker) { var expected = ".test-1 {background-image: url('ebay-logo-d481eb85.png');}\n.test-2 {background-image: url('ebay-logo-d481eb85.png');}\n.test-3 {background-image: url('ebay-logo-d481eb85.png');}\n.test-4 {background-image: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAE8AAAAeCAYAAABt5kPUAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAA2ZpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDpGNzdGMTE3NDA3MjA2ODExODhDNkIzODA1MTg5Nzc0NiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpGRTg2MEMyMzZFNzIxMUUyQjZGMEE2ODM0MDRENkNFNiIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpGRTg2MEMyMjZFNzIxMUUyQjZGMEE2ODM0MDRENkNFNiIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IE1hY2ludG9zaCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjAyODAxMTc0MDcyMDY4MTE5OTRDOTI2RkUxMEEyMEJFIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOkY3N0YxMTc0MDcyMDY4MTE4OEM2QjM4MDUxODk3NzQ2Ii8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+Yff30gAAE/BJREFUeNrsWgdwHdW5/s+Wu7u3q3fJspqLLMuVYINt3AgQwjPFYCCN+ogpLyRgCKQHAo/H8OAFhgAOxQQCDt3E2Bg74IK7ZMuyZCFbVpescnvZet5/9kpuhJbMvJkwb2d2tPferd/5/6+cFTHDESCiAPrBZjD7+oBqOpjHjoE5MACEFwAoBbZwGekgDh2D95QJ8HthGsjUAAKpheCGYYJ3b0fibwmd5nIERPx6A67L4KRF1SlU5jhgdrkTMj0C9IcN0A0K00oUEHkCPicP51S5QBIIuPg48Byet3OVfQ/UiADoIaBmDAjnAOCdQNVjAIIXvx8CIuUCWDoQORe2awPQFjsEeDjgkXifBM4uuwPSXaVg4j5w/L45SOpBSGgBe3t04QgPCT0AezqfxedS7d84wYLAUR+0bckHt1eG1tZWEOArLCrl4JtGK+wX8+CteC64iXESeJQLxoxCVaNZI6hmn3KwScGp8AicC9wyB5YF//IL91V2ZjVocRzc7DkM1WQINBwhrLKRFcf3xIdTV1zcLgEunOKzgUOg4euwcF/1AB148BlxuNnVCg5sNevL4IBVdn6NB4rSBLtNvy7LVwaPYP0lsH2neeKwNHMYt/kvQJvCTOS4ylwJ1K8RcJ8LHmFExiMwAg9EEGxRIewzx9sUrJkAV+aFoMRpgGaRv38SBKsgU4SFEzw2xzEBEFEMHCMrjy1NyAlK+OKRI6lbJkjVTDQ4MbWyz0ghx0/2f7R8SjCIQ0SgRFQyLY1Go8X4Nx2o5eKiUdOKxUK839/NeT1dpiiZaQ4KF/sG4L9juSAR+imCJNjWy85IA7+Tg56g4R2ImGM1w8hCBXQqDi6uGdCriOSw7OASo6r+aaKlKWA4CewNI1xI9WGUVuLHvwrwLg3MRBDB7CO8sxN4WTeZvAKhKQVlQ41qyQmfw+T0lOGzFRovRXBQCIoi27ZoAp/QoDwWkMCKCQdKOH48qyoETW9sWpDcum252d19NtqWDGR3QmlKGklqnyTn9x+QamueETPTn7+owpVcbwpwsFc9rV0tmD/JA1V5jsJnPgqsaOxJXhLTrDzKxILa/Y9FTCyXxB0NJczn51a6/ifTLQTo6Q+GFYY2xWsNbbneCuy8mmoDVWhJFJtsT/ZKRFSJI6NFSJ/1TH76zNf3q70PmVogEwE0TEuTA/G236Q7x240QT+NhpjoiadYFQEHKqAdre0N1f8nY2zTUuUxOdO3e8xpdx1q2XpOfkEur6pqXLAvLOENqmp69NkXntEbDy6hoz4Cb9BuVyHl96iOF1ZV2Uwkpse7u6dr9ftvybnthisvO6N6/89f6z4xdrjh9QrJbJ940YrVfc8OB/Q0+3uejN6xbV3w9FzENMe+sSP4q43N0RtvW5h5zaKJ7nW2qLCH4SW8r77pRusjq6kaGPOpFmYrq0xbtTSJJnon6V2vPTo23PCDAjmD1qv9UySsGt2MQ33ni9rYjHkbUxenI0VtIX24kUpcp5zagR7yo8MP3RWMty0SeRceH4N8z4wXnJXzoHn80ODWbVtkrL4wsQJBMEMhX+Dun72t7ambQ1xoPjUNhMKCFqGs7GUEb5tjSu2QfviIYg0PT7IGBy/RDjYvYFXI9nNkZ/UIv71vwXVNBe1v7RpuN030ebh4ndwAMUEIhfU0t1ccnlysvJbvEz6qLZL7mvpUX+ewPqOpV7302LBeBiKxhUUUifb0NYWXXzHD9yanDwKvduWpO5ZspdGOUpswcVCJq2Q3cZWu4pxj6jhvTcIc3OgDU51mRVuW0Wh7LdtPwP2GRG/yRYciayMiZ6GpXzL5qXNLM+atZ2B+Jo9xMgxEmya/smfZdjTUskl1yPZM+Pjs8jvPzvFXmI07huGee39qA8/fe8stEHniD/+V2PDBZZzXyyoQHNOmrnZf+/3zxNKS98E0Dztqa3rx+w4hP2+3cu6iFzi3O6w3NZ/LKtIMBDyewe6K3qlz31zXoi3Hs7rZ4GKacKkJUynOk3f++zkZi8+ucK3yO/n9U4udR3DcmypypA2LJ3pWRlSrpKNfq2EAWjrlt7bE586f6FudLx4JmS0P3mn2b7uIUR4THy5nzjpH5Z0LEIuPiZTdQfy1PZDsbeP8tVuFwiuepdqxGTR6tNxCAH1WUohzkt7O8RxrL2w9TA3D+eNyL3xhlIb+3sIjTXzY+sDDvaG6qQJWPgN9TvmdNxf6pzezM7W19sLmzZtTaqs1NFYmNmz8AQICNJkER82knRmPP/odPj09ru1rAO1gEyTWbwBtbx1oBxrBCobAe+vyR5Tzzn2c7U9cLlD31J87vuXjC0GW4ycnivICueW15SXfqi6Q25DX0KpYkMSVWZaoaoFX5mLPXVN49Xm1njcAPzMAB4J63gNrB++JJC0k5LjKp1Vu5HzVa7i0ce/zuRf8EiE0wIiihiUAwo0AiS6wjq3HLhjUhMJl9yJ3W/gjwxqmyAUNHimrgwEg8Ap0BrYvwHW+iNufVXX9kQO1nxxbt1TE1tXNBBT6Z3w8IW/JGsWRjgY/KxUERvfXdu5aQqMxF3GmTijPnfP75IYP1MjK58Boa7cTgrpjFx7EYVFRUHfuAv3wYRDHj3uJvLeeVRrmRQoZ+3dcpmRNg8RxY0zhe7PTHszyCAPsuGDcBAbIyE+giBxU5TrAg4njpnMyfvdeY/TbuBsPDgKbDob+7f0ZY+89P/PS+xR/x32WHZWRf5F/rPABHBjMtyQTL60hThpYoYNgDt0OfP6SZiL6u6g2VIyKC+l6ODAlZ+Erm3vfeNCB/Ghgrt3V/sxdRf4zNhK7mSmcbJSYaNR1rrpNM6KiA3mQKeq04mt/x/MOSzNi6PXxGKIfNwYCVtNZzMuxbziP29T27e9Wt32cSw1DIpJ0mjLhbtE4oLAYjqlT41h1QbQzfosXwNXdNlP2xuQE81yWCR6vECzPcaxr6VNt0PP9AgxFDfvCLPhne3j4pF+DZvzdwXO7xuVKO5u6kmcC/hZKGNl7u6CqWJ408A0fHq8nEbQGVOpENVUHS1BxMyDe5iGcJNF4h4Atg14iScyul7FDOR+MKKcRbfdMLsp9qsFddUMk2lzGKq59ePOio8ObzyvLXLBWM6NYG4JtRQRehp5Q3fRD/e9cxarOwDbP89a+Lwm+dw4f22QDLAgcODNjqK8OSKpJ5NbBoRLbgiDKaE245JZtbxPkjNOBO1Gr6H8wVej19bgtKDBSkWI8ksMlse54rx3H0l384e5ho7tjULfH1Y65IyUv4uWC2MbAVkhVYZZX2NdE4czROHegWy25eryxBWLN843eD25HQZgDZtJzWrGMToMAkE/7fWQOwR0+GKzJv/jRD5t/+RjjM8Z39V0v3jomY+5aVg4EeUzkFFtx93W9dKuKVSehAhNMUTUFVzzmU/Lt9h0V+IpyHmbPmwLr1mwBASvHBmBEu7FyOdfxz5+d0Rhwp7olQwdiIhj8KPGScAK9nn7yJAAe1xXQAVsZtJOimtPBQbqTD6T6gdkPAsMx3Tk29oefq+1//JU1ek1r5C8vonMVYmhikyB6NKqHkAdjVsoPc0VsfEbzkxn4GCZWrXiyxT/tmmPhfbWs+tqGPvxm2+CmC8qyFr5rUbQEKAy9wb21h/rXLHXY1iQBZZkL/zox7+I16BGBiifuVUJbt2zZ5bBx/XbkZIdDBz1lHLHawvj5t9bQUBy3PxtBtALMpoyOPmdpZm9eqTshuX6GhOYe2cub7xdx1xSzMKfBRKJjWMOWoJDm5NkcYIpnUSj6wkba8XhFnLAs99Wlzr4/nh0nI8gRQUe1XcUpeW8CpxzBXh6kWiApjLnORB9omb0bDS5nlouGG/cgmKV2JbKKTPaBUwvoNUXfffj9Az9aZRsXrL69nc/9pDRj7rus8lgC2X70iTtVPSyxCuQw8s0svfEhh+CB020NRTItr6iCpVdcjKCnp/fp4fB4e+IT/aHr0ovfULfvaNX21iOYLD+eiqGtsE4nyGfNspWWR36LUgEeS7/UHx9QfgJJzc1GfDBilDkEKPA7xW5qiwpAY08Mwkn0+Aja7HI3ZLl5G0gBzfPLO0O1x8uT06DaemU+VhpnA8r2qfzxT/jsxY9ZWEkU1Ra0EO4axlGRsc58wJdeBXzmvDS98U7fyaVOmBkf2gIVZTf/ubFz1Q97w/Vn2sob3DHv6PCWxRPzlqzvDu2deqj/3UsZ1zFLk++buhZv+W+HBzb+3dQtKxJUTMHHFErHbAfDsEfJCoVkfJJ5/t/8AlxXXm6DZKcLBiDyIsu80lmzIf2Rh8B3z12K6/JLs5Urr8jK/84l6QsrHB6qG9YoB4VDhr9r2Fg8s1SBkgwHxrcktqJpT2PFNQvahzS7GnO8AnQP69Mbu5MzmViAxUG2Mx6r8ParujWSbUVnmAiu1VaojqkA7sOsr4nFhRkcK0souQ7Eqnvwt9gMTCLpp0wQsKId/IixiTGl6Hv3s4FkSmtZBuzuWLnCwHPt6Vh5i45cx47jMarVFl51v4IDIglsVtv9qZXqIkwYPw4E9Gt/SW7ecgeeTWA8lvjrez9SFi98XT5nzjBRZMBUgUoXAQ6B5HJyQCwvA0wcELrvgUcSH2y6lshy1GkklLHfvvEmIs2K0fiIWUGQVm4ZvnvJNO9a5Lm+uEZtsEbFg3FhIG5BMQL7/LbAvZaKqDGmUAksnKy+WuiNz0xEYWKKJkwReJeT2DeO1BjvRChCwPkm29aFc5Xj9iQwelb/R6pQToBHWfeGG0CPtuC15qwpSjtjE3q9c1iVoRGeu+7gil8f6l97EWtX1qLlmQvfrcg+d4thal8418NJs76xV543509WDL0TSrDRdnRC8J5frNYPNueyxMH5fMD5Uf3xL5+eBlYgAJHHn/xx4r31N2LORbUe9Atez5G6jImbaFI/4T4RvNauZMVFj7W/VdeRqHAjMCKLTogcW9lnNM6+657r+tO6fZGLbOAwomVnCh13XFByN5WK2+0gwLjLUBUabrgpVUb8iGey7EkDrEisvm5Z23/bE9bg1gWnTLLRlMiQeBD4YB3wgheml1z/a7QlNJUyKL+v+6W7DSuZxvbFTGvNLv/Rg24pB1xSJq5Zn7sKrBUxMazQWw9PNj5prSWKwpLEfLO7p06orHiK83o2oucLkFhc1CKRKr3x4PfNox2LRr0h2hxLWr78HiWtqp/bfVS2Rm4ao1iPyhPlUEdi5v0BbXd1ofIiKuo6t0wGseLcb+8Lz97fmfx+36BWDA7Ojl+EJ9oDl2XfVlsm9Rv8dU/D4A/Pt0HCgTDan/sxCews5tJmPAlGpAfMBKWJbg8mi5lWYPdNVrS3msi+IeLwBdH7laU8kQc47yTkvThIaKxVzKklaWf+DfPt6y3H1l7ClJWzyZ6AbiUBBWS1yLs3dwb2fKkZRsLenrGpKHXrtqzIUyuf1ZsPXWDPXI7MWNicx6eAYqpMR+01iwMZ6QMZV19+7Uc157/z5H4oXLNruCmpodoiwZfmSn+cN9H90ut7wn8OBfXMlEcceafBTmGOXGPEgvi8Qt/tizKv/u6stA+KfCZylAZq3fUrzK5XH7AtCjfyPMSeyzLYfB0qh8jMHLslTvaHUHkvNAO7LrH6PrzNLlJXQZNQcftk1FfdQC8XyJiFY+5EZa8/Y03DrVuw+gSWKmw/QC26cNx9Z2W4yrYxg/xlFv4XP2VEa2BmDcQdlZV/5vNyt9KkKuN32QiCk1oW5jNttMrwJuUEn59XL02pfdh54bduFGpq6o+ADyxBcvVH9HEZXqHN7xU6a4uV9zH4r67MkVa5nHw8rNOCuE6x72nqxQdWE1YazfGLrVNLlUeWzvBfO61EafTIAmS6MO1YMQQ4uZXzVmNwVbOQ6/LRJ0g2UtRiUyw8a2kipfVwaZOfEUpv/C7nGddAE+05hFg+omS3ECXvIO+b/Fe8aZNwPJjOQjbvh+ev7B6OH5nUH2mYyIyzjtGrKveCl2eW3PAo84GKmPalVmJGorZvw1gGNBwGCwkfjTOYwaDfGhgqtwYH8oRJE71G6xGNc7kGkAPbhKKCTmqYFidjCikqhsi4GoiYPLy2K2i/GTPxASuyJZYaYPfROHgVHi2KJaPCVh05po0tzXK4WvrVWJpTaKstkptVgyaZ10OgobpAgaosC3gzDEbXy1itHlsArODePJroGg+cmA1ShkCDdRHOXdVNlKJm4J1RgsEdI1rKxjBetNsdRc4/PfUeFo1wCIHiMY4l9EDWOw3LtwfjHWM5HACek5JXzXz9zBxPdT3y3z84DT/amriiSQ5ymRm7mUTKs84ElVULjh77TJnxRZNMUWBYNzGHktDZew2aAg9/163UjDHrziQKARZassAn7sMQsm9epdt+Ac7eZ3D29JXF8u1nGPLUjBwqbS8o+b1ELsB2LEXK60HDlZt6l8HaDJNCamJUZ2Q0IijoPpDnVAQohsIi4N0yq1HX9cItg9GWsRIOjIZcODZrwes+pag+ovb9c+8wTk4RwOIWtjQzxsga+AA09bIFvvpbMHtcRkBlsY2BzOyjRU9xFp+zMED0FFAmVgdF2rOYP/3suTmCALJfVTkHWIUJeO+RZG/BgZ6/3MDakymuJHgTZZnzH+gO7jnlvwn+OfD+xReClchmS2JoOZIINMdeIyBn7+p4+o6o2pfDlFbDFp+Uu/T5MRlzGnQj/s+/PfvXX5jYYKuKXoh6qzGwOFJA4t/B6CcTDvWtuZ4nDrvKZNGfQN/3hAd9nSGq/w8e4wDVX4vrNPtfQHibA3lgbdrU9+aVlqWbiujvxjThrs6/ZGWOt7qBTXT+I8v/CjAAJOOJ0FaTyhsAAAAASUVORK5CYII=');}"; var actual = writerTracker.getCodeForPath(lassoPageResult.getCSSFiles()[0]); // console.log(actual); expect(actual).to.equal(expected); }; ================================================ FILE: test/autotests/util/caching-replay-stream/.gitignore ================================================ /build ================================================ FILE: test/autotests/util/caching-replay-stream/hello.txt ================================================ Hello World!! ================================================ FILE: test/autotests/util/caching-replay-stream/test.js ================================================ var expect = require('chai').expect; var fs = require('fs'); var nodePath = require('path'); exports.check = function (util) { var cachingStream = util.createCachingStream(); var outputDir = nodePath.join(__dirname, 'build'); require('../../../util').rmdirRecursive(outputDir); fs.mkdirSync(outputDir); var inFile = nodePath.join(__dirname, 'hello.txt'); var outFile1 = nodePath.join(outputDir, 'hello-1.txt'); var outFile2 = nodePath.join(outputDir, 'hello-2.txt'); var readStream = fs.createReadStream(inFile); var outStream = fs.createWriteStream(outFile1); return new Promise((resolve, reject) => { outStream.on('close', function() { outStream = fs.createWriteStream(outFile2); outStream.on('close', function() { var inTxt = fs.readFileSync(inFile, {encoding: 'utf8'}); var outFile1Txt = fs.readFileSync(outFile1, {encoding: 'utf8'}); var outFile2Txt = fs.readFileSync(outFile2, {encoding: 'utf8'}); expect(inTxt).to.equal(outFile1Txt); expect(inTxt).to.equal(outFile2Txt); resolve(); }); cachingStream.createReplayStream().pipe(outStream); }); readStream.pipe(cachingStream).pipe(outStream); }); }; ================================================ FILE: test/builtins-test.js ================================================ 'use strict'; require('./util/test-init'); var chai = require('chai'); chai.config.includeStack = true; require('chai').should(); var expect = require('chai').expect; var createLassoContext = require('./mock/create-lasso-context'); describe('lasso-require/builtins' , function() { it('should correctly resolve default builtins', function() { var fromDir = __dirname; var lassoContext = createLassoContext(); expect(lassoContext.resolve('assert', fromDir) != null).to.equal(true); expect(lassoContext.resolve('buffer', fromDir) != null).to.equal(true); expect(lassoContext.resolve('events', fromDir) != null).to.equal(true); expect(lassoContext.resolve('path', fromDir) != null).to.equal(true); expect(lassoContext.resolve('process', fromDir) != null).to.equal(true); expect(lassoContext.resolve('stream', fromDir) != null).to.equal(true); expect(lassoContext.resolve('util', fromDir) != null).to.equal(true); expect(lassoContext.resolve('lasso-loader', fromDir) != null).to.equal(true); expect(lassoContext.resolve('raptor-loader', fromDir) != null).to.equal(true); expect(lassoContext.resolve('string_decoder', fromDir) != null).to.equal(true); }); it('should allow custom builtins', function() { var fromDir = __dirname; var lassoContext = createLassoContext({ resolver: { builtins: { foo: require.resolve('./fixtures/builtin-foo') } } }); expect(lassoContext.resolve('foo', fromDir) != null).to.equal(true); expect(() => lassoContext.resolve('bar', fromDir)).to.throw(); }); it('should allow custom builtins (legacy)', function() { var fromDir = __dirname; var lassoContext = createLassoContext({ require: { builtins: { foo: require.resolve('./fixtures/builtin-foo') } } }); expect(lassoContext.resolve('foo', fromDir) != null).to.equal(true); expect(() => lassoContext.resolve('bar', fromDir)).to.throw(); }); // // it('should allow additional builtins', function() { // // var builtins = require('../lib/builtins').getBuiltins({ // foo: require.resolve('./fixtures/foo-shim') // }); // expect(builtins.buffer).to.contain('node_modules/buffer-browserify'); // expect(builtins.events).to.contain('node_modules/events'); // expect(builtins.foo).to.contain('foo-shim.js'); // // // console.log('builtins:', builtins); // }); }); ================================================ FILE: test/bundling-test.js ================================================ 'use strict'; require('./util/test-init'); const nodePath = require('path'); require('chai').config.includeStack = true; const WriterTracker = require('./util/WriterTracker'); const rmdirRecursive = require('./util').rmdirRecursive; const buildDir = nodePath.join(__dirname, 'build'); const lasso = require('lasso'); const Readable = require('stream').Readable; const urlReader = require('lasso/util/url-reader'); urlReader.createUrlReadStream = function(url) { var readable = new Readable(); readable.push('EXTERNAL:' + url); readable.push(null); return readable; }; describe('lasso/bundling' , function() { require('./autotest').scanDir( nodePath.join(__dirname, 'autotests/bundling'), async function (dir, helpers) { var main = require(nodePath.join(dir, 'test.js')); var testName = nodePath.basename(dir); var pageName = 'bundling-' + testName; var lassoConfig = main.getLassoConfig && main.getLassoConfig(); if (!lassoConfig) { lassoConfig = { bundlingEnabled: false, fingerprintsEnabled: false }; } if (!lassoConfig.outputDir) { lassoConfig.outputDir = nodePath.join(buildDir, pageName); } rmdirRecursive(lassoConfig.outputDir); var myLasso = lasso.create(lassoConfig, dir); var inputs; if (main.getInputs) { inputs = main.getInputs(); } else { let lassoOptions = main.getLassoOptions(dir) || {}; let check = main.check; inputs = [ { lassoOptions, check } ]; } for (const input of inputs) { var writerTracker = WriterTracker.create(myLasso.writer); var lassoOptions = input.lassoOptions; var check = input.check; var checkError = input.checkError; if (!lassoOptions.pageName) { lassoOptions.pageName = pageName; } if (!lassoOptions.from) { lassoOptions.from = dir; } let lassoPageResult; try { lassoPageResult = await myLasso.lassoPage(lassoOptions); } catch (err) { if (checkError) { checkError(err); } else { throw err; } } if (checkError) { throw new Error('Error expected'); } check(lassoPageResult, writerTracker, helpers); await lasso.flushAllCaches(); } }); }); ================================================ FILE: test/dep-require-test.js ================================================ 'use strict'; require('./util/test-init'); const nodePath = require('path'); const chai = require('chai'); chai.config.includeStack = true; require('chai').should(); const createLassoContext = require('./mock/create-lasso-context'); const normalizeOutput = require('./util/normalizeOutput'); const moduleSearchPath = require('./util/module-search-path'); const rootDir = nodePath.join(__dirname, '..'); describe('lasso-require/dependency-require' , function() { require('./autotest').scanDir( nodePath.join(__dirname, 'autotests/dep-require'), async function (dir, helpers) { var main = require(nodePath.join(dir, 'test.js')); var pluginConfig = main.getPluginConfig ? main.getPluginConfig() : {}; pluginConfig.rootDir = dir; var dependencyFactory = require('./mock/dependency-factory').create(pluginConfig); var patchedSearchPath; if (main.searchPath) { patchedSearchPath = moduleSearchPath.patchSearchPath(main.searchPath); } var dependencyDef = main.createDependency(dir); var dependency = dependencyFactory.depRequire(dependencyDef, dir); var lassoContext = createLassoContext(); dependency.init(lassoContext); try { let dependencies = await dependency.getDependencies(lassoContext); dependencies = normalizeOutput(dependencies, rootDir); if (patchedSearchPath) { patchedSearchPath.restore(); } helpers.compare(dependencies, '.json'); } catch (err) { if (patchedSearchPath) { patchedSearchPath.restore(); } throw err; } }); }); ================================================ FILE: test/dep-transport-define-test.js ================================================ 'use strict'; require('./util/test-init'); const nodePath = require('path'); const chai = require('chai'); chai.config.includeStack = true; require('chai').should(); const createLassoContext = require('./mock/create-lasso-context'); const moduleSearchPath = require('./util/module-search-path'); const normalizeOutput = require('./util/normalizeOutput'); describe('lasso-require/dep-transport-define' , function() { require('./autotest').scanDir( nodePath.join(__dirname, 'autotests/dep-transport-define'), async function (dir, helpers) { var main = require(nodePath.join(dir, 'test.js')); var dependencyProps = main.createDependency(dir); var patchedSearchPath; if (main.searchPath) { patchedSearchPath = moduleSearchPath.patchSearchPath(main.searchPath); } var pluginConfig = main.getPluginConfig ? main.getPluginConfig() : {}; pluginConfig.rootDir = dir; var dependencyFactory = require('./mock/dependency-factory').create(pluginConfig); var dependency = dependencyFactory.depRequire(dependencyProps); var lassoContext = createLassoContext(); dependency.init(lassoContext); let dependencies = await dependency.getDependencies(lassoContext); if (dependencies.dependencies) { dependencies = dependencies.dependencies; } let src; try { for (var i=0; i { var pluginConfig = { builtins: { foo: require.resolve('./fixtures/builtin-foo') } }; pluginConfig.rootDir = __dirname; var mockPluginConfig = buildPluginConfig(pluginConfig); var lassoContext = new MockLassoContext(); lassoContext.mockEnableCachingForCache('lasso-require/inspect'); var path = nodePath.join(__dirname, 'fixtures/inspect-cache/foo.js'); var readCount = 0; function createReadStream() { expect(arguments.length).to.equal(0); readCount++; return fs.createReadStream(path, { encoding: 'utf8' }); } function getLastModified() { return Promise.resolve(10); } let inspectResult = await inspectCache.inspectCached(path, {createReadStream, getLastModified}, lassoContext, mockPluginConfig); expect(readCount).to.equal(1); expect(inspectResult.fromCache).to.equal(undefined); // Inspect the same file... should come from the cache inspectResult = await inspectCache.inspectCached(path, {createReadStream, getLastModified}, lassoContext, mockPluginConfig); expect(inspectResult.fromCache).to.equal(true); expect(readCount).to.equal(1); }); it('should still read from cache without getLastModified', async () => { var pluginConfig = { builtins: { foo: require.resolve('./fixtures/builtin-foo') } }; pluginConfig.rootDir = __dirname; var mockPluginConfig = buildPluginConfig(pluginConfig); var lassoContext = new MockLassoContext(); lassoContext.mockEnableCachingForCache('lasso-require/inspect'); var path = nodePath.join(__dirname, 'fixtures/inspect-cache/foo.js'); var readCount = 0; function createReadStream() { expect(arguments.length).to.equal(0); readCount++; return fs.createReadStream(path, { encoding: 'utf8' }); } // TODO: Change should this be removed in this test? function getLastModified() { return Promise.resolve(-1); } let inspectResult = await inspectCache.inspectCached(path, {createReadStream, getLastModified}, lassoContext, mockPluginConfig); expect(readCount).to.equal(1); expect(inspectResult.fromCache).to.equal(undefined); // Inspect the same file... should come from the cache inspectResult = await inspectCache.inspectCached(path, {createReadStream, getLastModified}, lassoContext, mockPluginConfig); expect(inspectResult.fromCache).to.equal(true); expect(readCount).to.equal(2); }); }); ================================================ FILE: test/inspect-test.js ================================================ 'use strict'; require('./util/test-init'); const nodePath = require('path'); const chai = require('chai'); const stripAnsi = require('strip-ansi'); chai.config.includeStack = true; const fs = require('fs'); const inspect = require('lasso/require/util/inspect'); describe('lasso-require/util/inspect', function() { require('./autotest').scanDir( nodePath.join(__dirname, 'autotests/inspect'), async function (dir, helpers) { const inputPath = nodePath.join(dir, 'input.js'); const inputSrc = fs.readFileSync(inputPath, { encoding: 'utf8' }); try { const inspected = inspect(inputSrc, { filename: inputPath, allowShortcircuit: false }); helpers.compare(inspected, '.json'); } catch (err) { var message = stripAnsi(err.message); message = message.slice(message.indexOf("): ") + 3); helpers.compare({ name: err.name, message: message }, '.json'); } }); }); ================================================ FILE: test/lasso-image-test.js ================================================ 'use strict'; require('./util/test-init'); const chai = require('chai'); chai.Assertion.includeStack = true; require('chai').should(); const expect = require('chai').expect; const nodePath = require('path'); const fs = require('fs'); const lassoImagePlugin = require('lasso/plugins/lasso-image'); // Load this module just to make sure it works const lasso = require('lasso'); describe('lasso-image', function () { it('should allow for reading image info on the server', function(done) { const imgPath = require.resolve('./fixtures/ebay.png'); lassoImagePlugin.getImageInfo(imgPath, function (err, imageInfo) { if (err) return done(err); expect(imageInfo).to.deep.equal({ url: '/static/ebay-73498128.png', width: 174, height: 30 }); done(); }); }); it('should compile an image into a JavaScript module', async function() { var myLasso = lasso.create({ fileWriter: { fingerprintsEnabled: false, outputDir: nodePath.join(__dirname, 'static') }, bundlingEnabled: true, plugins: [ { plugin: lassoImagePlugin, config: { } } ] }); await myLasso.lassoPage({ name: 'testPage', dependencies: [ 'require: ./fixtures/ebay.png' ], from: module }); var output = fs.readFileSync(nodePath.join(__dirname, '/static/testPage.js'), {encoding: 'utf8'}); expect(output).to.contain('174'); expect(output).to.contain('30'); expect(output).to.contain('/static'); expect(output).to.contain('ebay.png'); return lasso.flushAllCaches(); }); it('should compile an image into a JavaScript module when not using require', async function() { var myLasso = lasso.create({ fileWriter: { fingerprintsEnabled: false, outputDir: nodePath.join(__dirname, 'static') }, bundlingEnabled: true, plugins: [ { plugin: lassoImagePlugin, config: {} } ] }); await myLasso.lassoPage({ name: 'testPage2', dependencies: [ './fixtures/ebay.png' ], from: module }); var output = fs.readFileSync(nodePath.join(__dirname, '/static/testPage.js'), {encoding: 'utf8'}); expect(output).to.contain('174'); expect(output).to.contain('30'); expect(output).to.contain('/static'); expect(output).to.contain('ebay.png'); return lasso.flushAllCaches(); }); it('should compile an image into a JavaScript module and retain lasso context', async function() { var myLasso = lasso.create({ fileWriter: { fingerprintsEnabled: false, outputDir: nodePath.join(__dirname, 'static') }, bundlingEnabled: true, plugins: [ { plugin: lassoImagePlugin, config: { } } ] }); var originalWriter = myLasso.writer; class Writer { async writeResource (reader, lassoContext) { var requestContext = lassoContext.data.renderContext.stream; var protocol = requestContext.secure ? 'https:' : 'http:'; return { url: protocol + '//static.example.com/ebay.png' }; } writeBundles() { return originalWriter.writeBundles.apply(originalWriter, arguments); } } myLasso.writer = new Writer(); myLasso.on('buildCacheKey', function(eventArgs) { var lassoContext = eventArgs.context; var requestContext = lassoContext.data.renderContext.stream; var cacheKey = eventArgs.cacheKey; if (requestContext.secure) { cacheKey.add('secure'); } }); var mockRenderContext = { stream: { secure: true } }; await myLasso.lassoPage({ name: 'testPage', dependencies: [ 'require: ./fixtures/ebay.png' ], from: module, data: { renderContext: mockRenderContext } }); var output = fs.readFileSync(nodePath.join(__dirname, '/static/testPage.js'), {encoding: 'utf8'}); expect(output).to.contain('174'); expect(output).to.contain('30'); expect(output).to.contain('https://static.example.com/ebay.png'); return lasso.flushAllCaches(); }); it('should allow passing the renderContext', function(done) { class Writer { async writeResource (reader, lassoContext) { var requestContext = lassoContext.data.renderContext.stream; var protocol = requestContext.secure ? 'https:' : 'http:'; return { url: protocol + '//static.example.com/ebay.png' }; } } var myLasso = lasso.create(); myLasso.writer = new Writer(); myLasso.on('buildCacheKey', function(eventArgs) { var lassoContext = eventArgs.context; var requestContext = lassoContext.data.renderContext.stream; var cacheKey = eventArgs.cacheKey; if (requestContext.secure) { cacheKey.add('secure'); } }); var mockRenderContext = { stream: { secure: true } }; lassoImagePlugin.getImageInfo(require.resolve('./fixtures/ebay.png'), { lasso: myLasso, renderContext: mockRenderContext }, function (err, imageInfo) { if (err) return done(err); expect(imageInfo).to.deep.equal({ url: 'https://static.example.com/ebay.png', width: 174, height: 30 }); done(); }); }); }); ================================================ FILE: test/load-prebuild-test.js ================================================ 'use strict'; require('./util/test-init'); const nodePath = require('path'); require('chai').config.includeStack = true; const lasso = require('lasso'); describe('lasso/flags', function() { require('./autotest').scanDir( nodePath.join(__dirname, 'autotests/load-prebuild'), async function (dir, helpers) { const main = require(nodePath.join(dir, 'test.js')); let lassoConfig = main.getLassoConfig && main.getLassoConfig(); if (!lassoConfig) { lassoConfig = { bundlingEnabled: false, fingerprintsEnabled: false }; } const myLasso = lasso.create(lassoConfig, dir); try { await main.check(myLasso); } catch (err) { if (main.checkError) { return main.checkError(err); } else { throw err; } } if (main.checkError) { throw new Error('Error expected'); } await lasso.flushAllCaches(); }); }); ================================================ FILE: test/mock/LassoManifest.js ================================================ var ok = require('assert').ok; var nodePath = require('path'); var condition = require('./condition'); var lassoResolveFrom = require('lasso-resolve-from'); function resolveBrowserPath(dir, path) { var resolved; if (path.charAt(0) === '.') { resolved = lassoResolveFrom(dir, path); } else { resolved = lassoResolveFrom(dir, './' + path); if (!resolved) { resolved = lassoResolveFrom(dir, path); } } return resolved ? resolved.path : undefined; } function LassoManifest(options) { if (options.manifest) { Object.assign(this, options.manifest); } if (options.dirname) { this.dirname = options.dirname; } if (options.filename) { this.filename = options.filename; } var dirname = this.dirname; ok(dirname, '"dirname" is required'); ok(typeof dirname === 'string', '"dirname" must be a string'); var requireRemap = this.requireRemap; if (requireRemap && Array.isArray(requireRemap)) { this.requireRemap = requireRemap.map((requireRemap) => { var from = resolveBrowserPath(dirname, requireRemap.from); var to = resolveBrowserPath(dirname, requireRemap.to); return { from: from, to: to, condition: condition.fromObject(requireRemap) }; }); } } LassoManifest.prototype = { __LassoManifest: true, getUniqueId: function() { return this._uniqueId; }, resolve: function(relPath) { return nodePath.resolve(this.dirname, relPath); }, getRequireRemap: function(lassoContext) { if (this.requireRemap && Array.isArray(this.requireRemap)) { var filteredRemaps = {}; var flags = lassoContext.flags; this.requireRemap.forEach(function(remap) { if (remap.condition && !remap.condition(flags)) { return; } filteredRemaps[remap.from] = remap.to; }); return filteredRemaps; } else { return {}; } } }; LassoManifest.isLassoManifest = function(o) { return o && o.__LassoManifest; }; module.exports = LassoManifest; ================================================ FILE: test/mock/MockDependency.js ================================================ 'use strict'; class MockDependency{ getParentManifestDir() { return this.__dirname; } getParentManifestPath() { return this.__filename; } } module.exports = MockDependency; ================================================ FILE: test/mock/MockLassoContext.js ================================================ 'use strict'; require('../util/test-init'); var fs = require('fs'); var nodePath = require('path'); var lassoCachingFS = require('lasso-caching-fs'); var Readable = require('stream').Readable; var util = require('util'); var fingerprintStream = require('./fingerprint-stream'); var MockMemoryCache = require('./MockMemoryCache'); var MockRequireHandler = require('./MockRequireHandler'); var LassoManifest = require('./LassoManifest'); var manifestLoader = require('./manifest-loader'); var resolve = require('lasso/resolve'); var getClientPath = require('lasso-modules-client/transport').getClientPath; function noop() {} function DeferredStream(startFn, options) { var self = this; Readable.call(this, options); // When _read is called, we need to start pushing data self._read = function() { self._read = noop; var stream = startFn.call(self); if (stream) { stream .on('data', function(data) { self.push(data); }) .on('end', function() { self.push(null); }) .on('error', function(err) { self.emit('error', err); }) .resume(); } }; return self; } util.inherits(DeferredStream, Readable); module.exports = DeferredStream; var MOCK_CACHE = { get: function(key, options) { return new Promise((resolve, reject) => { if (options.builder) { resolve(options.builder()); } else { resolve(); } }); }, getSync: function() { return; }, put: function(key, value, options) { }, putSync: function(key, value, options) { } }; class MockLassoContext { constructor() { this.data = {}; this.phaseData = {}; var requireExtensions = { js: { object: false, createReadStream: function(path, lassoContext) { return function() { return fs.createReadStream(path, {encoding: 'utf8'}); }; } }, json: { object: true, createReadStream: function(path, lassoContext) { return function() { return fs.createReadStream(path, {encoding: 'utf8'}); }; } } }; this.dependencyRegistry = { getRequireHandler: function(path, lassoContext) { var ext = nodePath.extname(path).substring(1); var requireExt = requireExtensions[ext]; return { object: requireExt.object === true, async init () { return Promise.resolve(); }, async getDependencies() { return []; }, createReadStream: requireExt.createReadStream(path, lassoContext), getLastModified: function() { return Promise.resolve(-1); } }; }, createRequireHandler: function(path, lassoContext, userOptions) { return new MockRequireHandler(userOptions, lassoContext, path); }, getRequireExtensionNames() { return Object.keys(requireExtensions).map((ext) => { return '.' + ext; }); } }; var mockCaches = this.mockCaches = {}; this.cache = { getCache(name) { return mockCaches[name] || MOCK_CACHE; }, getSyncCache(name) { return mockCaches[name] || MOCK_CACHE; } }; this.nextId = 0; } get isMockLassoContext() { return true; } /** * Converts a "reader" function to a function that *always* returns a stream. * The actual reader function may return a promise, a String, a stream or it may use a callback. */ createReadStream(func) { var stream = new DeferredStream(function() { // this function will be called when it is time to start reading data var finished = false; var callback = (err, value) => { if (finished) { return; } if (err) { stream.emit('error', err); return; } if (value == null) { stream.push(null); finished = true; } else { if (typeof value === 'string') { stream.push(value); stream.push(null); finished = true; } else if (typeof value.pipe === 'function') { // Looks like a stream... value.pipe(this); finished = true; } else if (typeof value.then === 'function') { // Looks like a promise... value .then((value) => { callback(null, value); }) .catch(callback); } else { // Hopefully a Buffer stream.push(value); stream.push(null); finished = true; } } }; var result = func(callback); if (!finished) { // callback was not invoked if (result === null) { callback(null, null); } else if (result === undefined) { // waiting on callback } else if (result && typeof result.pipe === 'function') { finished = true; return result; } else { callback(null, result); // A stream was returned, so we will return it } } }); return stream; } mockEnableCachingForCache(cacheName) { this.mockCaches[cacheName] = new MockMemoryCache(); } uniqueId() { return this.nextId++; } async getFileLastModified(path) { return Promise.resolve(-1); } isAsyncBundlingPhase() { return false; } get cachingFs() { return lassoCachingFS; } deferredStream(startFn, options) { return new DeferredStream(startFn, options); } createFingerprintStream() { return fingerprintStream.create(); } readPackageFile(path) { var rawManifest = manifestLoader.load(path); return new LassoManifest({ manifest: rawManifest, dependencyRegistry: this.dependencyRegistry }); } getResolver() { if (this.resolver === undefined) { this.resolver = resolve.createResolver(this, getClientPath); } return this.resolver; } resolve(targetModule, fromDir, options) { return this.getResolver().resolve(targetModule, fromDir, options); } resolveCached(targetModule, fromDir, options) { return this.getResolver().resolveCached(targetModule, fromDir, options); } } module.exports = MockLassoContext; ================================================ FILE: test/mock/MockMemoryCache.js ================================================ 'use strict'; class MockMemoryCache { constructor() { this.lookup = {}; } get(key, options) { var lookup = this.lookup; var lastModified = options && options.lastModified; if (!lastModified || lastModified < 0) { lastModified = null; } var entry = lookup[key]; if (entry && lastModified && entry.lastModified !== lastModified) { entry = null; } var value; if (!entry && options.builder) { value = options.builder(); entry = { value: value, lastModified: lastModified }; lookup[key] = entry; } return Promise.resolve(value); } getSync() { return this.get.apply(this, arguments); } } module.exports = MockMemoryCache; ================================================ FILE: test/mock/MockRequireHandler.js ================================================ 'use strict'; var nodePath = require('path'); var ok = require('assert').ok; var resolveFrom = require('resolve-from'); var EMPTY_ARRAY_PROMISE = Promise.resolve([]); class RequireHandler { constructor(userOptions, lassoContext, path) { ok(userOptions, '"userOptions" is required'); ok(lassoContext, '"lassoContext" is required'); ok(path, '"path" is required'); this.lassoContext = lassoContext; this.userOptions = userOptions; this.path = path; this.includePathArg = true; this.userThisObject = { path: path, resolvePath: function(pathToResolve) { var dir = nodePath.dirname(path); return resolveFrom(dir, pathToResolve); } }; this.lastModified = null; this.object = userOptions.object === true; } async init() { var lassoContext = this.lassoContext; var userInit = this.userOptions.init; return new Promise((resolve, reject) => { if (userInit) { var promise = userInit.call(this.userThisObject, lassoContext, (err) => { if (err) { reject(err); } else { resolve(); } }); if (promise !== undefined) { resolve(promise); } } else { resolve(); } }); } createReadStream() { var lassoContext = this.lassoContext; var path = this.path; var createReadStream = this.userOptions.createReadStream; if (createReadStream) { return this.includePathArg ? createReadStream.call(this.userThisObject, path, lassoContext) : createReadStream.call(this.userThisObject, lassoContext); } var userRead = this.userOptions.read; if (userRead) { return lassoContext.createReadStream((callback) => { return this.includePathArg ? userRead.call(this.userThisObject, path, lassoContext, callback) : userRead.call(this.userThisObject, lassoContext, callback); }); } else { return lassoContext.createReadStream((callback) => { callback(null, ''); }); } } async getLastModified() { var lassoContext = this.lassoContext; var path = this.path; var lastModifiedPromise = this.lastModified; if (lastModifiedPromise) { return lastModifiedPromise; } var userLastModified = this.userOptions.getLastModified; if (userLastModified) { this.lastModifiedPromise = new Promise((resolve, reject) => { let callback = (err, lastModified) => { if (err) { reject(err); } else { resolve(lastModified || -1); } }; var userPromise = this.includePathArg ? userLastModified.call(this.userThisObject, path, lassoContext, callback) : userLastModified.call(this.userThisObject, lassoContext, callback); if (userPromise !== undefined) { resolve(userPromise || -1); } }); } else { this.lastModifiedPromise = this.lassoContext.getFileLastModified(path); } return this.lastModifiedPromise; } getDependencies() { var lassoContext = this.lassoContext; var userGetDependencies = this.userOptions.getDependencies; if (!userGetDependencies) { return EMPTY_ARRAY_PROMISE; } console.log('THIS USER GETDEP: ', userGetDependencies); return userGetDependencies.call(this.userThisObject, lassoContext); } getDefaultBundleName(pageBundleName, lassoContext) { var userGetDefaultBundleName = this.userOptions.getDefaultBundleName; if (userGetDefaultBundleName) { return userGetDefaultBundleName.call(this.userThisObject, pageBundleName, lassoContext); } } } module.exports = RequireHandler; ================================================ FILE: test/mock/condition.js ================================================ exports.ifCondition = function(condition) { // This is a hack because we moved from "flags" to "extensions" // We create a function that introduces two scoped variables: flags, extensions var conditionFunc = eval("(function(flags, extensions) { return " + condition + ";})"); return function(flags) { return conditionFunc(flags, flags); }; }; exports.ifFlag = function(flag) { return function(flags) { return flags.contains(flag); }; }; exports.ifNotFlag = function(flag) { return function(flags) { return !flags.contains(flag); }; }; exports.fromObject = function(o) { var condition; if ((condition = o['if'])) { return exports.ifCondition(condition); } else if ((condition = o['if-flag'] || o['if-extension'])) { return exports.ifFlag(condition); } else if ((condition = o['if-not-flag'] || o['if-not-extension'])) { return exports.ifNotFlag(condition); } return null; }; ================================================ FILE: test/mock/create-lasso-context.js ================================================ "use strict"; require('../util/test-init'); const configLoader = require('lasso/config-loader'); var LassoContext = require('lasso/LassoContext'); var dependencies = require('lasso/dependencies'); var MockMemoryCache = require('./MockMemoryCache'); var fs = require('fs'); var nodePath = require('path'); var lassoCachingFS = require('lasso-caching-fs'); var Readable = require('stream').Readable; var util = require('util'); var fingerprintStream = require('./fingerprint-stream'); var MockMemoryCache = require('./MockMemoryCache'); var MockRequireHandler = require('./MockRequireHandler'); var LassoManifest = require('./LassoManifest'); var manifestLoader = require('./manifest-loader'); var MOCK_CACHE = { get: function(key, options) { return new Promise((resolve, reject) => { if (options.builder) { resolve(options.builder()); } else { resolve(); } }); }, put: function(key, value, options) { }, }; class SyncCache { constructor() { this._store = {}; } getSync(key) { return this._store[key]; } putSync(key, value) { this._store[key] = value; } } var requireExtensions = { js: { object: false, createReadStream: function(path, lassoContext) { return function() { return fs.createReadStream(path, {encoding: 'utf8'}); }; } }, json: { object: true, createReadStream: function(path, lassoContext) { return function() { return fs.createReadStream(path, {encoding: 'utf8'}); }; } } }; module.exports = function createLassoContext(config) { var lassoContext = new LassoContext(); var mockCaches = {}; var syncCaches = {}; var uniqueId = 0; lassoContext.isMockLassoContext = true; if (config) { const baseDir = process.cwd(); lassoContext.config = configLoader.load(config, baseDir, null); } lassoContext.dependencyRegistry = { __DependencyRegistry: true, getRequireHandler: function(path, lassoContext) { var ext = nodePath.extname(path).substring(1); var requireExt = requireExtensions[ext]; return { object: requireExt.object === true, async init() { return Promise.resolve(); }, async getDependencies() { return []; }, createReadStream: requireExt.createReadStream(path, lassoContext), getLastModified: function() { return Promise.resolve(-1); } }; }, createRequireHandler: function(path, lassoContext, userOptions) { return new MockRequireHandler(userOptions, lassoContext, path); }, getRequireExtensionNames() { return Object.keys(requireExtensions).map((ext) => { return '.' + ext; }); } } lassoContext.uniqueId = () => uniqueId++; lassoContext.cache = { getCache(name) { return mockCaches[name] || MOCK_CACHE; }, getSyncCache(name) { return syncCaches[name] || (syncCaches[name] = new SyncCache()); } }; lassoContext.mockEnableCachingForCache = (cacheName) => { mockCaches[cacheName] = new MockMemoryCache(); }; return lassoContext; } ================================================ FILE: test/mock/dependency-factory.js ================================================ 'use strict'; require('../util/test-init'); var mockLasso = require('./mock-lasso'); var buildPluginConfig = require('lasso/require/build-plugin-config'); var MockDependency = require('./MockDependency'); var extend = require('raptor-util/extend'); var fs = require('fs'); var path = require('path'); function removeDashes(str) { return str.replace(/-([a-z])/g, function (match, lower) { return lower.toUpperCase(); }); } function removeExt(filename) { var ext = path.extname(filename); return filename.slice(0, 0 - ext.length); } exports.create = function(pluginConfig) { var mockPluginConfig = buildPluginConfig(pluginConfig); function createDependencyFactory(mixinsFactory) { class Dependency extends MockDependency {} extend(Dependency.prototype, mixinsFactory.create(mockPluginConfig, mockLasso)); return function createDependency(props) { var d = new Dependency(); extend(d, props); return d; }; } var factories = {}; var srcDir = path.join(__dirname, '../../src/require'); fs.readdirSync(srcDir) .forEach(function(child) { if (child.startsWith('dep-')) { var name = removeDashes(removeExt(child)); factories[name] = createDependencyFactory(require(`lasso/require/${child}`)); } }); return factories; }; ================================================ FILE: test/mock/fingerprint-stream.js ================================================ 'use strict'; var crypto = require('crypto'); var inherit = require('raptor-util/inherit'); var Transform = require('stream').Transform; function FingerprintStream(options) { FingerprintStream.$super.call(this, options); this._shasum = crypto.createHash('sha1'); this._fingerprint = null; } FingerprintStream.prototype._transform = function(chunk, encoding, callback) { this._shasum.update(chunk); this.push(chunk); callback(); }; FingerprintStream.prototype._flush = function(callback) { this._fingerprint = this._shasum.digest('hex'); this._shasum = null; this.emit('fingerprint',this._fingerprint); callback(); }; FingerprintStream.prototype.on = function(event, callback) { if (event === 'fingerprint' && this._fingerprint) { callback.call(this, this._fingerprint); return this; } else { return FingerprintStream.$super.prototype.on.apply(this, arguments); } }; inherit(FingerprintStream, Transform); exports.create = function() { return new FingerprintStream(); }; ================================================ FILE: test/mock/manifest-loader.js ================================================ var ok = require('assert').ok; var nodePath = require('path'); var Module = require('module').Module; var fs = require('fs'); var resolveCache = {}; var manifestCache = {}; var isAbsolute = require('path').isAbsolute; var allowedProps = { dependencies: true, async: true, main: true, requireRemap: true }; function readManifest(path) { var manifest = manifestCache[path]; if (manifest !== undefined) { return manifest; } var json; try { json = fs.readFileSync(path, {encoding: 'utf8'}); } catch(e) { manifest = null; } if (json) { try { manifest = JSON.parse(json); } catch(e) { throw new Error('Unable to parse JSON file at path "' + path + '". Exception: ' + e); } for (var k in manifest) { if (manifest.hasOwnProperty(k)) { if (!allowedProps[k]) { throw new Error('Invalid property of "' + k + '" in lasso manifest at path "' + path + '"'); } } } if (manifest.main && manifest.dependencies) { throw new Error('"dependencies" not allowed when "main specified. Lasso.js manifest file: ' + path); } manifest.dirname = nodePath.dirname(path); manifest.filename = path; } manifestCache[path] = manifest; return manifest; } function tryManifest(dirname, manifestPath) { var manifest = readManifest(nodePath.resolve(dirname, manifestPath, 'browser.json')); if (!manifest) { manifest = readManifest(nodePath.resolve(dirname, manifestPath, 'optimizer.json')); } return manifest; } function tryExtensionManifest(dirname, manifestPath) { var manifest = readManifest(nodePath.resolve(dirname, manifestPath + '.browser.json')); if (!manifest) { manifest = readManifest(nodePath.resolve(dirname, manifestPath + '.optimizer.json')); } return manifest; } function tryQualified(dirname, manifestPath) { var path = nodePath.resolve(dirname, manifestPath); return readManifest(path); } function tryAll(dirname, manifestPath) { if (manifestPath.endsWith('browser.json') || manifestPath.endsWith('optimizer.json')) { return tryQualified(dirname, manifestPath); // //browser.json } else { return tryManifest(dirname, manifestPath) || // //browser.json tryExtensionManifest(dirname, manifestPath); // /.browser.json } } function _resolve(path, from) { if (isAbsolute(path)) { return tryAll(from, path); } if (!from) { throw new Error('"from" argument is required for non-absolute paths'); } var resolveKey = path + '|' + from; var manifest = resolveCache[resolveKey]; if (manifest !== undefined) { return manifest; } if (process.platform === 'win32') { path = path.replace(/\\/g, '/'); // Replace back slashes with forward slashes } if (path.startsWith('./') || path.startsWith('../')) { // Don't go through the search paths for relative paths manifest = tryAll(from, path); } else { var paths = Module._nodeModulePaths(from); for (var i=0, len=paths.length; i { let asyncPkg = new AsyncPackage('pkg'); expect(asyncPkg.getName()).to.equal('pkg'); }); it('should throw error if a bundle contains an invalid content type', () => { let asyncPkg = new AsyncPackage(); const bundle = new Bundle(); bundle.setContentType('INVALID_CONTENT_TYPE'); bundle.setUrl('./hello'); asyncPkg.addBundle(bundle); expect(() => { asyncPkg.getMeta(); }).to.throw('Invalid bundle content type: INVALID_CONTENT_TYPE'); }); it('should skip adding meta if content type is not defined', () => { let asyncPkg = new AsyncPackage(); const bundle = new Bundle(); let hasContentCalled = false; let getUrlCalled = false; sinon.stub(bundle, 'hasContent').callsFake(() => { hasContentCalled = true; return false; }); sinon.stub(bundle, 'getUrl').callsFake(() => { getUrlCalled = true; return true; }); bundle.setUrl('./hello'); asyncPkg.addBundle(bundle); expect(asyncPkg.getMeta()).to.deep.equal({}); expect(hasContentCalled).to.equal(true); expect(getUrlCalled).to.equal(false); }); it('should skip adding meta if url is not defined', () => { let asyncPkg = new AsyncPackage(); const bundle = new Bundle(); let hasContentCalled = false; let getUrlCalled = false; sinon.stub(bundle, 'hasContent').callsFake(() => { hasContentCalled = true; return true; }); sinon.stub(bundle, 'getUrl').callsFake(() => { getUrlCalled = true; return false; }); bundle.setUrl('./hello'); asyncPkg.addBundle(bundle); expect(asyncPkg.getMeta()).to.deep.equal({}); expect(hasContentCalled).to.equal(true); expect(getUrlCalled).to.equal(true); }); it('should skip adding meta if url is not defined', () => { let asyncPkg = new AsyncPackage(); const bundle = new Bundle(); bundle.setContentType('js'); asyncPkg.addBundle(bundle); expect(asyncPkg.getMeta()).to.deep.equal({}); }); }); ================================================ FILE: test/unit/LassoPageResult-test.js ================================================ require('../util/test-init'); const fs = require('fs'); const path = require('path'); const expect = require('chai').expect; const LassoPageResult = require('lasso/LassoPageResult'); describe('LassoPageResult test', function () { it('should deserialize from a reader stream', async () => { const filePath = path.resolve(__dirname, '../fixtures/file.json'); let fileContents = await fs.promises.readFile(filePath, 'utf8'); fileContents = JSON.parse(fileContents); let lassoPageResult = new LassoPageResult(); lassoPageResult = Object.assign(lassoPageResult, fileContents); const reader = () => fs.createReadStream(filePath); const result = await LassoPageResult.deserialize(reader); expect(result).to.deep.equal(lassoPageResult); }); }); ================================================ FILE: test/unit/hash-test.js ================================================ require('../util/test-init'); const expect = require('chai').expect; const hashUtil = require('lasso/util/hash'); describe('Hash test', function () { it('should generate full sha1 hash', () => { expect(hashUtil.generate('cats')).to.equal('8ebf601f8b808c32b8d2fb570c2e0fbdbb388add'); }); it('should return substring of generated hash', () => { expect(hashUtil.generate('cats', 5)).to.equal('8ebf6'); }); it('should return currect hash overflow', () => { expect(hashUtil.HASH_OVERFLOW_LENGTH).to.equal(8); }); }); ================================================ FILE: test/util/WriterTracker.js ================================================ var nodePath = require('path'); var fs = require('fs'); var ok = require('assert').ok; function WriterTracker(writer) { var _this = this; this.reset(); writer.on('resourceWritten', function(resource) { var outputFile = resource.outputFile; ok(outputFile, 'Output file expected'); _this._recordOutputFile(outputFile); }); writer.on('bundleWritten', function(info) { var bundle = info.bundle; var outputFile = bundle.outputFile; if (outputFile) { _this._recordOutputFile(outputFile); } }); } WriterTracker.prototype = { _recordOutputFile: function(outputFile) { var code = fs.readFileSync(outputFile, {encoding: 'utf8'}); this.outputFilesByPath[outputFile] = code; this.outputFilesByName[nodePath.basename(outputFile)] = code; }, getOutputPaths: function() { var paths = Object.keys(this.outputFilesByPath); paths.sort(); return paths; }, getOutputFilenames: function() { var filenames = Object.keys(this.outputFilesByName); filenames.sort(); return filenames; }, getCodeForFilename: function(filename) { var code = this.outputFilesByName[filename]; if (code) { code = code.replace(/\r\n/g, '\n'); } return code; }, getCodeForPath: function(path) { var code = this.outputFilesByPath[path]; if (code) { code = code.replace(/\r\n/g, '\n'); } return code; }, reset: function() { this.outputFilesByPath = {}; this.outputFilesByName = {}; }, toString: function() { return '[WriterTracker@' + module.filename + ']'; } }; WriterTracker.create = function(writer) { return new WriterTracker(writer); }; module.exports = WriterTracker; ================================================ FILE: test/util/index.js ================================================ var fs = require('fs'); var nodePath = require('path'); const vm = require('vm'); function rmdirRecursive(dir) { var filenames; try { filenames = fs.readdirSync(dir); } catch(e) { return; } filenames.forEach(function(filename) { var path = nodePath.join(dir, filename); if (fs.lstatSync(path).isDirectory()) { rmdirRecursive(path); } else { fs.unlinkSync(path); } }); fs.rmdirSync(dir); } function sandboxLoad(lassoPageResult, modulesRuntimeGlobal) { var sandbox = { }; sandbox.window = sandbox; var context = vm.createContext(sandbox); function loadScript(path) { var code = fs.readFileSync(path, {encoding: 'utf8'}); var script = new vm.Script(code, { filename: path, displayErrors: true }); script.runInContext(context); } var files = lassoPageResult.getOutputFilesWithInfo(); files.forEach((file) => { if (file.contentType !== 'js' || file.async) { return; } var path = file.path; try { loadScript(path); } catch (err) { console.error(`Error loading file ${JSON.stringify(file)}`, err); throw err; } }); modulesRuntimeGlobal = modulesRuntimeGlobal || '$_mod'; vm.runInContext(`window.${modulesRuntimeGlobal} && ${modulesRuntimeGlobal}.ready()`, context); sandbox.$loadScript = sandbox.window.$loadScript = loadScript; sandbox.console = sandbox.window.console = console; return sandbox; } function writeTestHtmlPage(lassoPageResult, outputFile) { var headHtml = lassoPageResult.getHeadHtml(); var bodyHtml = lassoPageResult.getBodyHtml(); var htmlSrc = 'Test Page' + headHtml + '' + bodyHtml + ''; fs.writeFileSync(outputFile, htmlSrc, { encoding: 'utf8' }); } exports.rmdirRecursive = rmdirRecursive; exports.sandboxLoad = sandboxLoad; exports.writeTestHtmlPage = writeTestHtmlPage; ================================================ FILE: test/util/module-search-path.js ================================================ var Module = require('module').Module; var oldNodeModulePaths = Module._nodeModulePaths; exports.patchSearchPath = function(dir) { Module._nodeModulePaths = function(from) { var paths = oldNodeModulePaths.call(this, from); paths = paths.concat([dir]); return paths; }; return { restore: function() { Module._nodeModulePaths = oldNodeModulePaths; } }; }; ================================================ FILE: test/util/normalizeOutput.js ================================================ var path = require('path'); function normalizeOutput(o, dir, options) { if (dir && typeof dir === 'object') { options = dir; dir = null; } var replaceVersions = options && options.replaceVersions === true; dir = dir || process.cwd(); var parentDir = path.dirname(dir); function helper(o) { if (Array.isArray(o)) { return o.map(helper); } else if (typeof o === 'object') { for (var k in o) { if (o.hasOwnProperty(k)) { var v = o[k]; if (/^_[a-f0-9]{6}$/.test(k)) { delete o[k]; k = '_HASH'; } o[k] = helper(v); } } } else if (typeof o === 'string') { o = o.split(dir).join(''); o = o.split(parentDir).join(''); o = o.split(process.cwd()).join(''); o = o.replace(/lasso-loader\$[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/g, 'lasso-loader$x.x.x'); o = o.replace(/_[a-f0-9]{6}/, "_HASH"); if (replaceVersions) { o = o.replace(/[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/g, 'x.x.x'); } } return o; } return helper(o); } module.exports = normalizeOutput; ================================================ FILE: test/util/patch-module.js ================================================ const fs = require('fs'); const nodePath = require('path'); const Module = require('module').Module; const oldResolveFilename = Module._resolveFilename; const rootDir = nodePath.join(__dirname, '../../'); const lassoDir = nodePath.join(rootDir, 'src'); const lassoInstalledDir = nodePath.join(rootDir, 'node_modules/lasso'); if (fs.existsSync(lassoInstalledDir)) { fs.renameSync(lassoInstalledDir, nodePath.join(rootDir, 'node_modules/~lasso')); } function buildPath (request, beginPath) { return nodePath.join(beginPath, request.substring('lasso/'.length)); } Module._resolveFilename = function(request, parent, isMain) { if (request.charAt(0) !== '.') { if (request === 'lasso/node-require-no-op' || request.startsWith('lasso/dist-compat/') || request.startsWith('lasso/src') || request.startsWith('lasso/middleware') || request.startsWith('lasso/browser-refresh')) { request = buildPath(request, rootDir); } else if (request === 'lasso') { request = rootDir; } else if (request.startsWith('lasso/')) { request = buildPath(request, lassoDir); } } return oldResolveFilename.call(this, request, parent, isMain); }; ================================================ FILE: test/util/test-init.js ================================================ require('./patch-module'); ================================================ FILE: test/util-test.js ================================================ 'use strict'; require('./util/test-init'); const nodePath = require('path'); require('chai').config.includeStack = true; const util = require('lasso/util'); describe('lasso/util', function() { require('./autotest').scanDir( nodePath.join(__dirname, 'autotests/util'), async function (dir, helpers) { var main = require(nodePath.join(dir, 'test.js')); return main.check(util); }); }); ================================================ FILE: test-loop.sh ================================================ #!/bin/bash set -e COUNTER=0 while [ $COUNTER -lt 100 ]; do echo The counter is $COUNTER npm test # rm -rf .cache/ static/ && mocha test/lasso-test.js let COUNTER=COUNTER+1 done