applying transfer to react app

This commit is contained in:
Tyler Koenig
2021-09-20 16:54:47 -04:00
parent 8819f31dd0
commit c612b7d702
37373 changed files with 3775588 additions and 2871 deletions
+19
View File
@@ -0,0 +1,19 @@
Copyright 2018 Google LLC
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.
+1
View File
@@ -0,0 +1 @@
This module's documentation can be found at https://developers.google.com/web/tools/workbox/modules/workbox-build
+3
View File
@@ -0,0 +1,3 @@
"use strict";
require("./_version.mjs");
+6
View File
@@ -0,0 +1,6 @@
{
"origin": "https://storage.googleapis.com",
"bucketName": "workbox-cdn",
"releasesDir": "releases",
"latestVersion": "5.1.4"
}
+239
View File
@@ -0,0 +1,239 @@
"use strict";
/*
Copyright 2018 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
const upath = require('upath');
const generateSWSchema = require('./options/schema/generate-sw');
const getFileManifestEntries = require('./lib/get-file-manifest-entries');
const rebasePath = require('./lib/rebase-path');
const validate = require('./lib/validate-options');
const writeServiceWorkerUsingDefaultTemplate = require('./lib/write-sw-using-default-template'); // eslint-disable-next-line jsdoc/newline-after-description
/**
* This method creates a list of URLs to precache, referred to as a "precache
* manifest", based on the options you provide.
*
* It also takes in additional options that configures the service worker's
* behavior, like any `runtimeCaching` rules it should use.
*
* Based on the precache manifest and the additional configuration, it writes
* a ready-to-use service worker file to disk at `swDest`.
*
* @param {Object} config The configuration to use.
*
* @param {string} config.globDirectory The local directory you wish to match
* `globPatterns` against. The path is relative to the current directory.
*
* @param {string} config.swDest The path and filename of the service worker file
* that will be created by the build process, relative to the current working
* directory. It must end in '.js'.
*
* @param {Array<module:workbox-build.ManifestEntry>} [config.additionalManifestEntries]
* A list of entries to be precached, in addition to any entries that are
* generated as part of the build configuration.
*
* @param {Array<string>} [config.babelPresetEnvTargets=['chrome >= 56']]
* The [targets](https://babeljs.io/docs/en/babel-preset-env#targets) to pass to
* `babel-preset-env` when transpiling the service worker bundle.
*
* @param {string} [config.cacheId] An optional ID to be prepended to cache
* names. This is primarily useful for local development where multiple sites
* may be served from the same `http://localhost:port` origin.
*
* @param {boolean} [config.cleanupOutdatedCaches=false] Whether or not Workbox
* should attempt to identify an delete any precaches created by older,
* incompatible versions.
*
* @param {boolean} [config.clientsClaim=false] Whether or not the service
* worker should [start controlling](https://developers.google.com/web/fundamentals/primers/service-workers/lifecycle#clientsclaim)
* any existing clients as soon as it activates.
*
* @param {string} [config.directoryIndex='index.html'] If a navigation request
* for a URL ending in `/` fails to match a precached URL, this value will be
* appended to the URL and that will be checked for a precache match. This
* should be set to what your web server is using for its directory index.
*
* @param {RegExp} [config.dontCacheBustURLsMatching] Assets that match this will be
* assumed to be uniquely versioned via their URL, and exempted from the normal
* HTTP cache-busting that's done when populating the precache. While not
* required, it's recommended that if your existing build process already
* inserts a `[hash]` value into each filename, you provide a RegExp that will
* detect that, as it will reduce the bandwidth consumed when precaching.
*
* @param {boolean} [config.globFollow=true] Determines whether or not symlinks
* are followed when generating the precache manifest. For more information, see
* the definition of `follow` in the `glob`
* [documentation](https://github.com/isaacs/node-glob#options).
*
* @param {Array<string>} [config.globIgnores=['node_modules/**']]
* A set of patterns matching files to always exclude when generating the
* precache manifest. For more information, see the definition of `ignore` in the `glob`
* [documentation](https://github.com/isaacs/node-glob#options).
*
* @param {Array<string>} [config.globPatterns=['**.{js,css,html}']]
* Files matching any of these patterns will be included in the precache
* manifest. For more information, see the
* [`glob` primer](https://github.com/isaacs/node-glob#glob-primer).
*
* @param {boolean} [config.globStrict=true] If true, an error reading a directory when
* generating a precache manifest will cause the build to fail. If false, the
* problematic directory will be skipped. For more information, see the
* definition of `strict` in the `glob`
* [documentation](https://github.com/isaacs/node-glob#options).
*
* @param {Array<RegExp>} [config.ignoreURLParametersMatching=[/^utm_/]]
* Any search parameter names that match against one of the RegExp in this array
* will be removed before looking for a precache match. This is useful if your
* users might request URLs that contain, for example, URL parameters used to
* track the source of the traffic.
*
* @param {Array<string>} [config.importScripts] A list of JavaScript files that
* should be passed to [`importScripts()`](https://developer.mozilla.org/en-US/docs/Web/API/WorkerGlobalScope/importScripts)
* inside the generated service worker file. This is useful when you want to
* let Workbox create your top-level service worker file, but want to include
* some additional code, such as a push event listener.
*
* @param {boolean} [config.inlineWorkboxRuntime=false] Whether the runtime code
* for the Workbox library should be included in the top-level service worker,
* or split into a separate file that needs to be deployed alongside the service
* worker. Keeping the runtime separate means that users will not have to
* re-download the Workbox code each time your top-level service worker changes.
*
* @param {Array<module:workbox-build.ManifestTransform>} [config.manifestTransforms] One or more
* functions which will be applied sequentially against the generated manifest.
* If `modifyURLPrefix` or `dontCacheBustURLsMatching` are also specified, their
* corresponding transformations will be applied first.
*
* @param {number} [config.maximumFileSizeToCacheInBytes=2097152] This value can be
* used to determine the maximum size of files that will be precached. This
* prevents you from inadvertently precaching very large files that might have
* accidentally matched one of your patterns.
*
* @param {string} [config.mode='production'] If set to 'production', then an
* optimized service worker bundle that excludes debugging info will be
* produced. If not explicitly configured here, the `process.env.NODE_ENV` value
* will be used, and failing that, it will fall back to `'production'`.
*
* @param {object<string, string>} [config.modifyURLPrefix] A mapping of prefixes
* that, if present in an entry in the precache manifest, will be replaced with
* the corresponding value. This can be used to, for example, remove or add a
* path prefix from a manifest entry if your web hosting setup doesn't match
* your local filesystem setup. As an alternative with more flexibility, you can
* use the `manifestTransforms` option and provide a function that modifies the
* entries in the manifest using whatever logic you provide.
*
* @param {string} [config.navigateFallback] If specified, all
* [navigation requests](https://developers.google.com/web/fundamentals/primers/service-workers/high-performance-loading#first_what_are_navigation_requests)
* for URLs that aren't precached will be fulfilled with the HTML at the URL
* provided. You must pass in the URL of an HTML document that is listed in your
* precache manifest. This is meant to be used in a Single Page App scenario, in
* which you want all navigations to use common [App Shell HTML](https://developers.google.com/web/fundamentals/architecture/app-shell).
*
* @param {Array<RegExp>} [config.navigateFallbackDenylist] An optional array
* of regular expressions that restricts which URLs the configured
* `navigateFallback` behavior applies to. This is useful if only a subset of
* your site's URLs should be treated as being part of a
* [Single Page App](https://en.wikipedia.org/wiki/Single-page_application). If
* both `navigateFallbackDenylist` and `navigateFallbackAllowlist` are
* configured, the denylist takes precedent.
*
* @param {Array<RegExp>} [config.navigateFallbackAllowlist] An optional array
* of regular expressions that restricts which URLs the configured
* `navigateFallback` behavior applies to. This is useful if only a subset of
* your site's URLs should be treated as being part of a
* [Single Page App](https://en.wikipedia.org/wiki/Single-page_application). If
* both `navigateFallbackDenylist` and `navigateFallbackAllowlist` are
* configured, the denylist takes precedent.
*
* @param {boolean} [config.navigationPreload=false] Whether or not to enable
* [navigation preload](https://developers.google.com/web/tools/workbox/modules/workbox-navigation-preload)
* in the generated service worker. When set to true, you must also use
* `runtimeCaching` to set up an appropriate response strategy that will match
* navigation requests, and make use of the preloaded response.
*
* @param {boolean|Object} [config.offlineGoogleAnalytics=false] Controls
* whether or not to include support for
* [offline Google Analytics](https://developers.google.com/web/tools/workbox/guides/enable-offline-analytics).
* When `true`, the call to `workbox-google-analytics`'s `initialize()` will be
* added to your generated service worker. When set to an `Object`, that object
* will be passed in to the `initialize()` call, allowing you to customize the
* behavior.
*
* @param {Array<RuntimeCachingEntry>} [config.runtimeCaching]
*
* @param {boolean} [config.skipWaiting=false] Whether to add an
* unconditional call to [`skipWaiting()`]{@link module:workbox-core.skipWaiting}
* to the generated service worker. If `false`, then a `message` listener will
* be added instead, allowing you to conditionally call `skipWaiting()`.
*
* @param {boolean} [config.sourcemap=true] Whether to create a sourcemap
* for the generated service worker files.
*
* @param {Object} [config.templatedURLs] If a URL is rendered based on some
* server-side logic, its contents may depend on multiple files or on some other
* unique string value. The keys in this object are server-rendered URLs. If the
* values are an array of strings, they will be interpreted as `glob` patterns,
* and the contents of any files matching the patterns will be used to uniquely
* version the URL. If used with a single string, it will be interpreted as
* unique versioning information that you've generated for a given URL.
*
* @return {Promise<{count: number, filePaths: Array<string>, size: number, warnings: Array<string>}>}
* A promise that resolves once the service worker and related files
* (indicated by `filePaths`) has been written to `swDest`. The `size` property
* contains the aggregate size of all the precached entries, in bytes, and the
* `count` property contains the total number of precached entries. Any
* non-fatal warning messages will be returned via `warnings`.
*
* @memberof module:workbox-build
*/
async function generateSW(config) {
const options = validate(config, generateSWSchema);
if (options.globDirectory) {
// Make sure we leave swDest out of the precache manifest.
options.globIgnores.push(rebasePath({
baseDirectory: options.globDirectory,
file: options.swDest
})); // If we create an extra external runtime file, ignore that, too.
// See https://rollupjs.org/guide/en/#outputchunkfilenames for naming.
if (!options.inlineWorkboxRuntime) {
const swDestDir = upath.dirname(options.swDest);
const workboxRuntimeFile = upath.join(swDestDir, 'workbox-*.js');
options.globIgnores.push(rebasePath({
baseDirectory: options.globDirectory,
file: workboxRuntimeFile
}));
}
}
const {
count,
size,
manifestEntries,
warnings
} = await getFileManifestEntries(options);
const filePaths = await writeServiceWorkerUsingDefaultTemplate(Object.assign({
manifestEntries
}, options));
return {
count,
filePaths,
size,
warnings
};
}
module.exports = generateSW;
+116
View File
@@ -0,0 +1,116 @@
"use strict";
/*
Copyright 2018 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
const getFileManifestEntries = require('./lib/get-file-manifest-entries');
const getManifestSchema = require('./options/schema/get-manifest');
const validate = require('./lib/validate-options'); // eslint-disable-next-line jsdoc/newline-after-description
/**
* This method returns a list of URLs to precache, referred to as a "precache
* manifest", along with details about the number of entries and their size,
* based on the options you provide.
*
* @param {Object} config The configuration to use.
*
* @param {string} config.globDirectory The local directory you wish to match
* `globPatterns` against. The path is relative to the current directory.
*
* @param {Array<module:workbox-build.ManifestEntry>} [config.additionalManifestEntries]
* A list of entries to be precached, in addition to any entries that are
* generated as part of the build configuration.
*
* @param {RegExp} [config.dontCacheBustURLsMatching] Assets that match this will be
* assumed to be uniquely versioned via their URL, and exempted from the normal
* HTTP cache-busting that's done when populating the precache. While not
* required, it's recommended that if your existing build process already
* inserts a `[hash]` value into each filename, you provide a RegExp that will
* detect that, as it will reduce the bandwidth consumed when precaching.
*
* @param {boolean} [config.globFollow=true] Determines whether or not symlinks
* are followed when generating the precache manifest. For more information, see
* the definition of `follow` in the `glob`
* [documentation](https://github.com/isaacs/node-glob#options).
*
* @param {Array<string>} [config.globIgnores=['node_modules/**']]
* A set of patterns matching files to always exclude when generating the
* precache manifest. For more information, see the definition of `ignore` in the `glob`
* [documentation](https://github.com/isaacs/node-glob#options).
*
* @param {Array<string>} [config.globPatterns=['**.{js,css,html}']]
* Files matching any of these patterns will be included in the precache
* manifest. For more information, see the
* [`glob` primer](https://github.com/isaacs/node-glob#glob-primer).
*
* @param {boolean} [config.globStrict=true] If true, an error reading a directory when
* generating a precache manifest will cause the build to fail. If false, the
* problematic directory will be skipped. For more information, see the
* definition of `strict` in the `glob`
* [documentation](https://github.com/isaacs/node-glob#options).
*
* @param {Array<module:workbox-build.ManifestTransform>} [config.manifestTransforms] One or more
* functions which will be applied sequentially against the generated manifest.
* If `modifyURLPrefix` or `dontCacheBustURLsMatching` are also specified, their
* corresponding transformations will be applied first.
*
* @param {number} [config.maximumFileSizeToCacheInBytes=2097152] This value can be
* used to determine the maximum size of files that will be precached. This
* prevents you from inadvertently precaching very large files that might have
* accidentally matched one of your patterns.
*
* @param {string} [config.mode='production'] If set to 'production', then an
* optimized service worker bundle that excludes debugging info will be
* produced. If not explicitly configured here, the `process.env.NODE_ENV` value
* will be used, and failing that, it will fall back to `'production'`.
*
* @param {object<string, string>} [config.modifyURLPrefix] A mapping of prefixes
* that, if present in an entry in the precache manifest, will be replaced with
* the corresponding value. This can be used to, for example, remove or add a
* path prefix from a manifest entry if your web hosting setup doesn't match
* your local filesystem setup. As an alternative with more flexibility, you can
* use the `manifestTransforms` option and provide a function that modifies the
* entries in the manifest using whatever logic you provide.
*
* @param {Object} [config.templatedURLs] If a URL is rendered based on some
* server-side logic, its contents may depend on multiple files or on some other
* unique string value. The keys in this object are server-rendered URLs. If the
* values are an array of strings, they will be interpreted as `glob` patterns,
* and the contents of any files matching the patterns will be used to uniquely
* version the URL. If used with a single string, it will be interpreted as
* unique versioning information that you've generated for a given URL.
*
* @return {Promise<{count: number, manifestEntries: Array<module:workbox-build.ManifestEntry>, size: number, warnings: Array<string>}>}
* A promise that resolves once the precache manifest (available in the
* `manifestEntries` property) has been determined. The `size` property
* contains the aggregate size of all the precached entries, in bytes, and the
* `count` property contains the total number of precached entries. Any
* non-fatal warning messages will be returned via `warnings`.
*
* @memberof module:workbox-build
*/
async function getManifest(config) {
const options = validate(config, getManifestSchema);
const {
manifestEntries,
count,
size,
warnings
} = await getFileManifestEntries(options);
return {
manifestEntries,
count,
size,
warnings
};
}
module.exports = getManifest;
+32
View File
@@ -0,0 +1,32 @@
"use strict";
/*
Copyright 2018 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
const {
getModuleURL
} = require('./lib/cdn-utils');
const copyWorkboxLibraries = require('./lib/copy-workbox-libraries');
const generateSW = require('./generate-sw');
const getManifest = require('./get-manifest');
const injectManifest = require('./inject-manifest');
/**
* @module workbox-build
*/
module.exports = {
copyWorkboxLibraries,
generateSW,
getManifest,
getModuleURL,
injectManifest
};
+228
View File
@@ -0,0 +1,228 @@
"use strict";
/*
Copyright 2018 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
const assert = require('assert');
const fse = require('fs-extra');
const sourceMapURL = require('source-map-url');
const stringify = require('fast-json-stable-stringify');
const upath = require('upath');
const errors = require('./lib/errors');
const escapeRegexp = require('./lib/escape-regexp');
const getFileManifestEntries = require('./lib/get-file-manifest-entries');
const injectManifestSchema = require('./options/schema/inject-manifest');
const rebasePath = require('./lib/rebase-path');
const replaceAndUpdateSourceMap = require('./lib/replace-and-update-source-map');
const validate = require('./lib/validate-options'); // eslint-disable-next-line jsdoc/newline-after-description
/**
* This method creates a list of URLs to precache, referred to as a "precache
* manifest", based on the options you provide.
*
* The manifest is injected into the `swSrc` file, and the placeholder string
* `injectionPoint` determines where in the file the manifest should go.
*
* The final service worker file, with the manifest injected, is written to
* disk at `swDest`.
*
* @param {Object} config The configuration to use.
*
* @param {string} config.globDirectory The local directory you wish to match
* `globPatterns` against. The path is relative to the current directory.
*
* @param {string} config.swDest The path and filename of the service worker file
* that will be created by the build process, relative to the current working
* directory. It must end in '.js'.
*
* @param {string} config.swSrc The path and filename of the service worker file
* that will be read during the build process, relative to the current working
* directory.
*
* @param {Array<module:workbox-build.ManifestEntry>} [config.additionalManifestEntries]
* A list of entries to be precached, in addition to any entries that are
* generated as part of the build configuration.
*
* @param {RegExp} [config.dontCacheBustURLsMatching] Assets that match this will be
* assumed to be uniquely versioned via their URL, and exempted from the normal
* HTTP cache-busting that's done when populating the precache. While not
* required, it's recommended that if your existing build process already
* inserts a `[hash]` value into each filename, you provide a RegExp that will
* detect that, as it will reduce the bandwidth consumed when precaching.
*
* @param {boolean} [config.globFollow=true] Determines whether or not symlinks
* are followed when generating the precache manifest. For more information, see
* the definition of `follow` in the `glob`
* [documentation](https://github.com/isaacs/node-glob#options).
*
* @param {Array<string>} [config.globIgnores=['node_modules/**']]
* A set of patterns matching files to always exclude when generating the
* precache manifest. For more information, see the definition of `ignore` in the `glob`
* [documentation](https://github.com/isaacs/node-glob#options).
*
* @param {Array<string>} [config.globPatterns=['**.{js,css,html}']]
* Files matching any of these patterns will be included in the precache
* manifest. For more information, see the
* [`glob` primer](https://github.com/isaacs/node-glob#glob-primer).
*
* @param {boolean} [config.globStrict=true] If true, an error reading a directory when
* generating a precache manifest will cause the build to fail. If false, the
* problematic directory will be skipped. For more information, see the
* definition of `strict` in the `glob`
* [documentation](https://github.com/isaacs/node-glob#options).
*
* @param {string} [config.injectionPoint='self.__WB_MANIFEST'] The string to
* find inside of the `swSrc` file. Once found, it will be replaced by the
* generated precache manifest.
*
* @param {Array<module:workbox-build.ManifestTransform>} [config.manifestTransforms] One or more
* functions which will be applied sequentially against the generated manifest.
* If `modifyURLPrefix` or `dontCacheBustURLsMatching` are also specified, their
* corresponding transformations will be applied first.
*
* @param {number} [config.maximumFileSizeToCacheInBytes=2097152] This value can be
* used to determine the maximum size of files that will be precached. This
* prevents you from inadvertently precaching very large files that might have
* accidentally matched one of your patterns.
*
* @param {string} [config.mode='production'] If set to 'production', then an
* optimized service worker bundle that excludes debugging info will be
* produced. If not explicitly configured here, the `process.env.NODE_ENV` value
* will be used, and failing that, it will fall back to `'production'`.
*
* @param {object<string, string>} [config.modifyURLPrefix] A mapping of prefixes
* that, if present in an entry in the precache manifest, will be replaced with
* the corresponding value. This can be used to, for example, remove or add a
* path prefix from a manifest entry if your web hosting setup doesn't match
* your local filesystem setup. As an alternative with more flexibility, you can
* use the `manifestTransforms` option and provide a function that modifies the
* entries in the manifest using whatever logic you provide.
*
* @param {Object} [config.templatedURLs] If a URL is rendered based on some
* server-side logic, its contents may depend on multiple files or on some other
* unique string value. The keys in this object are server-rendered URLs. If the
* values are an array of strings, they will be interpreted as `glob` patterns,
* and the contents of any files matching the patterns will be used to uniquely
* version the URL. If used with a single string, it will be interpreted as
* unique versioning information that you've generated for a given URL.
*
* @return {Promise<{count: number, filePaths: Array<string>, size: number, warnings: Array<string>}>}
* A promise that resolves once the service worker and related files
* (indicated by `filePaths`) has been written to `swDest`. The `size` property
* contains the aggregate size of all the precached entries, in bytes, and the
* `count` property contains the total number of precached entries. Any
* non-fatal warning messages will be returned via `warnings`.
*
* @memberof module:workbox-build
*/
async function injectManifest(config) {
const options = validate(config, injectManifestSchema); // Make sure we leave swSrc and swDest out of the precache manifest.
for (const file of [options.swSrc, options.swDest]) {
options.globIgnores.push(rebasePath({
file,
baseDirectory: options.globDirectory
}));
}
const globalRegexp = new RegExp(escapeRegexp(options.injectionPoint), 'g');
const {
count,
size,
manifestEntries,
warnings
} = await getFileManifestEntries(options);
let swFileContents;
try {
swFileContents = await fse.readFile(options.swSrc, 'utf8');
} catch (error) {
throw new Error(`${errors['invalid-sw-src']} ${error.message}`);
}
const injectionResults = swFileContents.match(globalRegexp);
if (!injectionResults) {
// See https://github.com/GoogleChrome/workbox/issues/2230
if (upath.resolve(options.swSrc) === upath.resolve(options.swDest)) {
throw new Error(errors['same-src-and-dest'] + ' ' + options.injectionPoint);
}
throw new Error(errors['injection-point-not-found'] + ' ' + options.injectionPoint);
}
assert(injectionResults.length === 1, errors['multiple-injection-points'] + options.injectionPoint);
const manifestString = stringify(manifestEntries);
const filesToWrite = {};
const url = sourceMapURL.getFrom(swFileContents); // If our swSrc file contains a sourcemap, we would invalidate that
// mapping if we just replaced injectionPoint with the stringified manifest.
// Instead, we need to update the swDest contents as well as the sourcemap
// at the same time.
// See https://github.com/GoogleChrome/workbox/issues/2235
if (url) {
const sourcemapSrcPath = upath.resolve(upath.dirname(options.swSrc), url);
const sourcemapDestPath = upath.resolve(upath.dirname(options.swDest), url);
let originalMap;
try {
originalMap = await fse.readJSON(sourcemapSrcPath, 'utf8');
} catch (error) {
throw new Error(`${errors['cant-find-sourcemap']} ${error.message}`);
}
const {
map,
source
} = await replaceAndUpdateSourceMap({
originalMap,
jsFilename: upath.basename(options.swDest),
originalSource: swFileContents,
replaceString: manifestString,
searchString: options.injectionPoint
});
filesToWrite[options.swDest] = source;
filesToWrite[sourcemapDestPath] = map;
} else {
// If there's no sourcemap associated with swSrc, a simple string
// replacement will suffice.
filesToWrite[options.swDest] = swFileContents.replace(globalRegexp, manifestString);
}
for (const [file, contents] of Object.entries(filesToWrite)) {
try {
await fse.mkdirp(upath.dirname(file));
} catch (error) {
throw new Error(errors['unable-to-make-injection-directory'] + ` '${error.message}'`);
}
await fse.writeFile(file, contents);
}
return {
count,
size,
warnings,
// Use upath.resolve() to make all the paths absolute.
filePaths: Object.keys(filesToWrite).map(f => upath.resolve(f))
};
}
module.exports = injectManifest;
@@ -0,0 +1,44 @@
"use strict";
/*
Copyright 2019 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
const errors = require('./errors');
module.exports = additionalManifestEntries => {
return manifest => {
const warnings = [];
const stringEntries = new Set();
for (const additionalEntry of additionalManifestEntries) {
// Warn about either a string or an object that lacks a precache property.
// (An object with a revision property set to null is okay.)
if (typeof additionalEntry === 'string') {
stringEntries.add(additionalEntry);
} else if (additionalEntry && additionalEntry.revision === undefined) {
stringEntries.add(additionalEntry.url);
}
manifest.push(additionalEntry);
}
if (stringEntries.size > 0) {
let urls = '\n';
for (const stringEntry of stringEntries) {
urls += ` - ${stringEntry}\n`;
}
warnings.push(errors['string-entry-warning'] + urls);
}
return {
manifest,
warnings
};
};
};
+138
View File
@@ -0,0 +1,138 @@
"use strict";
/*
Copyright 2019 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
const {
rollup
} = require('rollup');
const {
terser
} = require('rollup-plugin-terser');
const {
writeFile
} = require('fs-extra');
const babel = require('rollup-plugin-babel');
const omt = require('@surma/rollup-plugin-off-main-thread');
const upath = require('upath');
const presetEnv = require('@babel/preset-env');
const replace = require('@rollup/plugin-replace');
const resolve = require('@rollup/plugin-node-resolve');
const tempy = require('tempy');
module.exports = async ({
babelPresetEnvTargets,
inlineWorkboxRuntime,
mode,
sourcemap,
swDest,
unbundledCode
}) => {
// We need to write this to the "real" file system, as Rollup won't read from
// a custom file system.
const {
dir,
base
} = upath.parse(swDest);
const temporaryFile = tempy.file({
name: base
});
await writeFile(temporaryFile, unbundledCode);
const plugins = [resolve(), replace({
'process.env.NODE_ENV': JSON.stringify(mode)
}), babel({
// Disable the logic that checks for local Babel config files:
// https://github.com/GoogleChrome/workbox/issues/2111
babelrc: false,
configFile: false,
presets: [[presetEnv, {
targets: {
browsers: babelPresetEnvTargets
},
loose: true
}]]
})];
if (mode === 'production') {
plugins.push(terser({
mangle: {
toplevel: true,
properties: {
regex: /(^_|_$)/
}
}
}));
}
const rollupConfig = {
plugins,
input: temporaryFile
}; // Rollup will inline the runtime by default. If we don't want that, we need
// to add in some additional config.
if (!inlineWorkboxRuntime) {
rollupConfig.plugins.unshift(omt());
rollupConfig.manualChunks = id => {
return id.includes('workbox') ? 'workbox' : undefined;
};
}
const bundle = await rollup(rollupConfig);
const {
output
} = await bundle.generate({
sourcemap,
// Using an external Workbox runtime requires 'amd'.
format: inlineWorkboxRuntime ? 'es' : 'amd'
});
const files = [];
for (const chunkOrAsset of output) {
if (chunkOrAsset.isAsset) {
files.push({
name: chunkOrAsset.fileName,
contents: chunkOrAsset.source
});
} else {
let code = chunkOrAsset.code;
if (chunkOrAsset.map) {
const sourceMapFile = chunkOrAsset.fileName + '.map';
code += `//# sourceMappingURL=${sourceMapFile}\n`;
files.push({
name: sourceMapFile,
contents: chunkOrAsset.map.toString()
});
}
files.push({
name: chunkOrAsset.fileName,
contents: code
});
}
} // Make sure that if there was a directory portion included in swDest, it's
// preprended to all of the generated files.
return files.map(file => {
file.name = upath.format({
dir,
base: file.name
});
return file;
});
};
+45
View File
@@ -0,0 +1,45 @@
"use strict";
/*
Copyright 2018 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
const assert = require('assert');
const cdn = require('../cdn-details.json');
const errors = require('./errors');
const getCDNOrigin = () => {
return `${cdn.origin}/${cdn.bucketName}/${cdn.releasesDir}`;
};
const getVersionedCDNURL = () => {
return `${getCDNOrigin()}/${cdn.latestVersion}`;
};
const getModuleURL = (moduleName, buildType) => {
assert(moduleName, errors['no-module-name']);
if (buildType) {
const pkgJson = require(`${moduleName}/package.json`);
if (buildType === 'dev' && pkgJson.workbox.prodOnly) {
// This is not due to a public-facing exception, so just throw an Error(),
// without creating an entry in errors.js.
throw Error(`The 'dev' build of ${moduleName} is not available.`);
}
return `${getVersionedCDNURL()}/${moduleName}.${buildType.slice(0, 4)}.js`;
}
return `${getVersionedCDNURL()}/${moduleName}.js`;
};
module.exports = {
getCDNOrigin,
getModuleURL
};
+69
View File
@@ -0,0 +1,69 @@
"use strict";
/*
Copyright 2018 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
const fse = require('fs-extra');
const upath = require('upath');
const errors = require('./errors'); // Used to filter the libraries to copy based on our package.json dependencies.
const WORKBOX_PREFIX = 'workbox-'; // The directory within each package containing the final bundles.
const BUILD_DIR = 'build';
/**
* This copies over a set of runtime libraries used by Workbox into a
* local directory, which should be deployed alongside your service worker file.
*
* As an alternative to deploying these local copies, you could instead use
* Workbox from its official CDN URL.
*
* This method is exposed for the benefit of developers using
* [injectManifest()]{@link module:workbox-build.injectManifest} who would
* prefer not to use the CDN copies of Workbox. Developers using
* [generateSW()]{@link module:workbox-build.generateSW} don't need to
* explicitly call this method.
*
* @param {string} destDirectory The path to the parent directory under which
* the new directory of libraries will be created.
* @return {Promise<string>} The name of the newly created directory.
*
* @alias module:workbox-build.copyWorkboxLibraries
*/
module.exports = async destDirectory => {
const thisPkg = require('../../package.json'); // Use the version string from workbox-build in the name of the parent
// directory. This should be safe, because lerna will bump workbox-build's
// pkg.version whenever one of the dependent libraries gets bumped, and we
// care about versioning the dependent libraries.
const workboxDirectoryName = `workbox-v${thisPkg.version}`;
const workboxDirectoryPath = upath.join(destDirectory, workboxDirectoryName);
await fse.ensureDir(workboxDirectoryPath);
const copyPromises = [];
const librariesToCopy = Object.keys(thisPkg.dependencies).filter(dependency => dependency.startsWith(WORKBOX_PREFIX));
for (const library of librariesToCopy) {
// Get the path to the package on the user's filesystem by require-ing
// the package's `package.json` file via the node resolution algorithm.
const libraryPath = upath.dirname(require.resolve(`${library}/package.json`));
const buildPath = upath.join(libraryPath, BUILD_DIR); // fse.copy() copies all the files in a directory, not the directory itself.
// See https://github.com/jprichardson/node-fs-extra/blob/master/docs/copy.md#copysrc-dest-options-callback
copyPromises.push(fse.copy(buildPath, workboxDirectoryPath));
}
try {
await Promise.all(copyPromises);
return workboxDirectoryName;
} catch (error) {
throw Error(`${errors['unable-to-copy-workbox-libraries']} ${error}`);
}
};
+118
View File
@@ -0,0 +1,118 @@
"use strict";
/*
Copyright 2018 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
const ol = require('common-tags').oneLine;
module.exports = {
'unable-to-get-rootdir': `Unable to get the root directory of your web app.`,
'no-extension': ol`Unable to detect a usable extension for a file in your web
app directory.`,
'invalid-file-manifest-name': ol`The File Manifest Name must have at least one
character.`,
'unable-to-get-file-manifest-name': 'Unable to get a file manifest name.',
'invalid-sw-dest': `The 'swDest' value must be a valid path.`,
'unable-to-get-sw-name': 'Unable to get a service worker file name.',
'unable-to-get-save-config': ol`An error occurred when asking to save details
in a config file.`,
'unable-to-get-file-hash': ol`An error occurred when attempting to create a
file hash.`,
'unable-to-get-file-size': ol`An error occurred when attempting to get a file
size.`,
'unable-to-glob-files': 'An error occurred when globbing for files.',
'unable-to-make-manifest-directory': ol`Unable to make output directory for
file manifest.`,
'read-manifest-template-failure': 'Unable to read template for file manifest',
'populating-manifest-tmpl-failed': ol`An error occurred when populating the
file manifest template.`,
'manifest-file-write-failure': 'Unable to write the file manifest.',
'unable-to-make-sw-directory': ol`Unable to make the directories to output
the service worker path.`,
'read-sw-template-failure': ol`Unable to read the service worker template
file.`,
'sw-write-failure': 'Unable to write the service worker file.',
'sw-write-failure-directory': ol`Unable to write the service worker file;
'swDest' should be a full path to the file, not a path to a directory.`,
'unable-to-copy-workbox-libraries': ol`One or more of the Workbox libraries
could not be copied over to the destination directory: `,
'invalid-generate-sw-input': ol`The input to generateSW() must be an object.`,
'invalid-glob-directory': ol`The supplied globDirectory must be a path as a
string.`,
'invalid-dont-cache-bust': ol`The supplied 'dontCacheBustURLsMatching'
parameter must be a RegExp.`,
'invalid-exclude-files': 'The excluded files should be an array of strings.',
'invalid-get-manifest-entries-input': ol`The input to
'getFileManifestEntries()' must be an object.`,
'invalid-manifest-path': ol`The supplied manifest path is not a string with
at least one character.`,
'invalid-manifest-entries': ol`The manifest entries must be an array of
strings or JavaScript objects containing a url parameter.`,
'invalid-manifest-format': ol`The value of the 'format' option passed to
generateFileManifest() must be either 'iife' (the default) or 'es'.`,
'invalid-static-file-globs': ol`The 'globPatterns' value must be an array
of strings.`,
'invalid-templated-urls': ol`The 'templatedURLs' value should be an object
that maps URLs to either a string, or to an array of glob patterns.`,
'templated-url-matches-glob': ol`One of the 'templatedURLs' URLs is already
being tracked via 'globPatterns': `,
'invalid-glob-ignores': ol`The 'globIgnores' parameter must be an array of
glob pattern strings.`,
'manifest-entry-bad-url': ol`The generated manifest contains an entry without
a URL string. This is likely an error with workbox-build.`,
'modify-url-prefix-bad-prefixes': ol`The 'modifyURLPrefix' parameter must be
an object with string key value pairs.`,
'invalid-inject-manifest-arg': ol`The input to 'injectManifest()' must be an
object.`,
'injection-point-not-found': ol`Unable to find a place to inject the manifest.
Please ensure that your service worker file contains the following: `,
'multiple-injection-points': ol`Please ensure that your 'swSrc' file contains
only one match for the following: `,
'populating-sw-tmpl-failed': ol`Unable to generate service worker from
template.`,
'useless-glob-pattern': ol`One of the glob patterns doesn't match any files.
Please remove or fix the following: `,
'bad-template-urls-asset': ol`There was an issue using one of the provided
'templatedURLs'.`,
'invalid-runtime-caching': ol`The 'runtimeCaching' parameter must an an
array of objects with at least a 'urlPattern' and 'handler'.`,
'static-file-globs-deprecated': ol`'staticFileGlobs' is deprecated.
Please use 'globPatterns' instead.`,
'dynamic-url-deprecated': ol`'dynamicURLToDependencies' is deprecated.
Please use 'templatedURLs' instead.`,
'urlPattern-is-required': ol`The 'urlPattern' option is required when using
'runtimeCaching'.`,
'handler-is-required': ol`The 'handler' option is required when using
runtimeCaching.`,
'invalid-generate-file-manifest-arg': ol`The input to generateFileManifest()
must be an Object.`,
'invalid-sw-src': `The 'swSrc' file can't be read.`,
'same-src-and-dest': ol`Unable to find a place to inject the manifest. This is
likely because swSrc and swDest are configured to the same file.
Please ensure that your swSrc file contains the following:`,
'only-regexp-routes-supported': ol`Please use a regular expression object as
the urlPattern parameter. (Express-style routes are not currently
supported.)`,
'bad-runtime-caching-config': ol`An unknown configuration option was used
with runtimeCaching: `,
'invalid-network-timeout-seconds': ol`When using networkTimeoutSeconds, you
must set the handler to 'NetworkFirst'.`,
'no-module-name': ol`You must provide a moduleName parameter when calling
getModuleURL().`,
'bad-manifest-transforms-return-value': ol`The return value from a
manifestTransform should be an object with 'manifest' and optionally
'warnings' properties.`,
'string-entry-warning': ol`Some items were passed to additionalManifestEntries
without revisioning info. This is generally NOT safe. Learn more at
https://bit.ly/wb-precache.`,
'no-manifest-entries-or-runtime-caching': ol`Couldn't find configuration for
either precaching or runtime caching. Please ensure that the various glob
options are set to match one or more files, and/or configure the
runtimeCaching option.`,
'cant-find-sourcemap': ol`The swSrc file refers to a sourcemap that can't be
opened:`
};
+11
View File
@@ -0,0 +1,11 @@
"use strict";
/*
Copyright 2019 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
module.exports = str => str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+29
View File
@@ -0,0 +1,29 @@
"use strict";
/*
Copyright 2018 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
const crypto = require('crypto');
module.exports = (compositeURL, dependencyDetails) => {
let totalSize = 0;
let compositeHash = '';
for (const fileDetails of dependencyDetails) {
totalSize += fileDetails.size;
compositeHash += fileDetails.hash;
}
const md5 = crypto.createHash('md5');
md5.update(compositeHash);
const hashOfHashes = md5.digest('hex');
return {
file: compositeURL,
hash: hashOfHashes,
size: totalSize
};
};
+70
View File
@@ -0,0 +1,70 @@
"use strict";
/*
Copyright 2018 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
const glob = require('glob');
const upath = require('upath');
const errors = require('./errors');
const getFileSize = require('./get-file-size');
const getFileHash = require('./get-file-hash');
module.exports = ({
globDirectory,
globFollow,
globIgnores,
globPattern,
globStrict
}) => {
let globbedFiles;
let warning;
try {
globbedFiles = glob.sync(globPattern, {
cwd: globDirectory,
follow: globFollow,
ignore: globIgnores,
strict: globStrict
});
} catch (err) {
throw new Error(errors['unable-to-glob-files'] + ` '${err.message}'`);
}
if (globbedFiles.length === 0) {
warning = errors['useless-glob-pattern'] + ' ' + JSON.stringify({
globDirectory,
globPattern,
globIgnores
}, null, 2);
}
const fileDetails = globbedFiles.map(file => {
const fullPath = upath.join(globDirectory, file);
const fileSize = getFileSize(fullPath);
if (fileSize === null) {
return null;
}
const fileHash = getFileHash(fullPath);
return {
file: `${upath.relative(globDirectory, fullPath)}`,
hash: fileHash,
size: fileSize
};
}); // If !== null, means it's a valid file.
const globbedFileDetails = fileDetails.filter(details => details !== null);
return {
globbedFileDetails,
warning
};
};
+23
View File
@@ -0,0 +1,23 @@
"use strict";
/*
Copyright 2018 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
const fs = require('fs');
const getStringHash = require('./get-string-hash');
const errors = require('./errors');
module.exports = file => {
try {
const buffer = fs.readFileSync(file);
return getStringHash(buffer);
} catch (err) {
throw new Error(errors['unable-to-get-file-hash'] + ` '${err.message}'`);
}
};
+128
View File
@@ -0,0 +1,128 @@
"use strict";
/*
Copyright 2018 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
const assert = require('assert');
const errors = require('./errors');
const transformManifest = require('./transform-manifest');
const getCompositeDetails = require('./get-composite-details');
const getFileDetails = require('./get-file-details');
const getStringDetails = require('./get-string-details');
module.exports = async ({
additionalManifestEntries,
dontCacheBustURLsMatching,
globDirectory,
globFollow,
globIgnores,
globPatterns,
globStrict,
manifestTransforms,
maximumFileSizeToCacheInBytes,
modifyURLPrefix,
swDest,
templatedURLs
}) => {
const warnings = []; // Initialize to an empty array so that we can still pass something to
// transformManifest() and get a normalized output.
let fileDetails = [];
const fileSet = new Set();
if (globDirectory) {
try {
fileDetails = globPatterns.reduce((accumulated, globPattern) => {
const {
globbedFileDetails,
warning
} = getFileDetails({
globDirectory,
globFollow,
globIgnores,
globPattern,
globStrict
});
if (warning) {
warnings.push(warning);
}
globbedFileDetails.forEach(fileDetails => {
if (fileSet.has(fileDetails.file)) {
return;
}
fileSet.add(fileDetails.file);
accumulated.push(fileDetails);
});
return accumulated;
}, []);
} catch (error) {
// If there's an exception thrown while globbing, then report
// it back as a warning, and don't consider it fatal.
warnings.push(error.message);
}
}
if (templatedURLs) {
for (const url of Object.keys(templatedURLs)) {
assert(!fileSet.has(url), errors['templated-url-matches-glob']);
const dependencies = templatedURLs[url];
if (Array.isArray(dependencies)) {
const details = dependencies.reduce((previous, globPattern) => {
try {
const {
globbedFileDetails,
warning
} = getFileDetails({
globDirectory,
globFollow,
globIgnores,
globPattern,
globStrict
});
if (warning) {
warnings.push(warning);
}
return previous.concat(globbedFileDetails);
} catch (error) {
const debugObj = {};
debugObj[url] = dependencies;
throw new Error(`${errors['bad-template-urls-asset']} ` + `'${globPattern}' from '${JSON.stringify(debugObj)}':\n` + error);
}
}, []);
fileDetails.push(getCompositeDetails(url, details));
} else if (typeof dependencies === 'string') {
fileDetails.push(getStringDetails(url, dependencies));
}
}
}
const transformedManifest = await transformManifest({
additionalManifestEntries,
dontCacheBustURLsMatching,
fileDetails,
manifestTransforms,
maximumFileSizeToCacheInBytes,
modifyURLPrefix
});
if (warnings.length > 0) {
transformedManifest.warnings.push(...warnings);
}
return transformedManifest;
};
+26
View File
@@ -0,0 +1,26 @@
"use strict";
/*
Copyright 2018 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
const fs = require('fs');
const errors = require('./errors');
module.exports = file => {
try {
const stat = fs.statSync(file);
if (!stat.isFile()) {
return null;
}
return stat.size;
} catch (err) {
throw new Error(errors['unable-to-get-file-size'] + ` '${err.message}'`);
}
};
+18
View File
@@ -0,0 +1,18 @@
"use strict";
/*
Copyright 2018 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
const getStringHash = require('./get-string-hash');
module.exports = (url, string) => {
return {
file: url,
hash: getStringHash(string),
size: string.length
};
};
+16
View File
@@ -0,0 +1,16 @@
"use strict";
/*
Copyright 2018 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
const crypto = require('crypto');
module.exports = string => {
const md5 = crypto.createHash('md5');
md5.update(string);
return md5.digest('hex');
};
+28
View File
@@ -0,0 +1,28 @@
"use strict";
/*
Copyright 2018 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
const prettyBytes = require('pretty-bytes');
module.exports = maximumFileSizeToCacheInBytes => {
return originalManifest => {
const warnings = [];
const manifest = originalManifest.filter(entry => {
if (entry.size <= maximumFileSizeToCacheInBytes) {
return true;
}
warnings.push(`${entry.url} is ${prettyBytes(entry.size)}, and won't ` + `be precached. Configure maximumFileSizeToCacheInBytes to change ` + `this limit.`);
return false;
});
return {
manifest,
warnings
};
};
};
+52
View File
@@ -0,0 +1,52 @@
"use strict";
/*
Copyright 2018 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
const errors = require('./errors');
const escapeRegExp = require('./escape-regexp');
module.exports = modifyURLPrefix => {
if (!modifyURLPrefix || typeof modifyURLPrefix !== 'object' || Array.isArray(modifyURLPrefix)) {
throw new Error(errors['modify-url-prefix-bad-prefixes']);
} // If there are no entries in modifyURLPrefix, just return an identity
// function as a shortcut.
if (Object.keys(modifyURLPrefix).length === 0) {
return entry => entry;
}
Object.keys(modifyURLPrefix).forEach(key => {
if (typeof modifyURLPrefix[key] !== 'string') {
throw new Error(errors['modify-url-prefix-bad-prefixes']);
}
}); // Escape the user input so it's safe to use in a regex.
const safeModifyURLPrefixes = Object.keys(modifyURLPrefix).map(escapeRegExp); // Join all the `modifyURLPrefix` keys so a single regex can be used.
const prefixMatchesStrings = safeModifyURLPrefixes.join('|'); // Add `^` to the front the prefix matches so it only matches the start of
// a string.
const modifyRegex = new RegExp(`^(${prefixMatchesStrings})`);
return originalManifest => {
const manifest = originalManifest.map(entry => {
if (typeof entry.url !== 'string') {
throw new Error(errors['manifest-entry-bad-url']);
}
entry.url = entry.url.replace(modifyRegex, match => {
return modifyURLPrefix[match];
});
return entry;
});
return {
manifest
};
};
};
+86
View File
@@ -0,0 +1,86 @@
"use strict";
/*
Copyright 2019 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
const ol = require('common-tags').oneLine;
const upath = require('upath');
/**
* Class for keeping track of which Workbox modules are used by the generated
* service worker script.
*
* @private
*/
class ModuleRegistry {
/**
* @private
*/
constructor() {
this.modulesUsed = new Map();
}
/**
* @return {Array<string>} A list of all of the import statements that are
* needed for the modules being used.
* @private
*/
getImportStatements() {
const workboxModuleImports = [];
for (const [localName, {
moduleName,
pkg
}] of this.modulesUsed) {
// By default require.resolve returns the resolved path of the 'main'
// field, which might be deeper than the package root. To work around
// this, we can find the package's root by resolving its package.json and
// strip the '/package.json' from the resolved path.
const pkgJsonPath = require.resolve(`${pkg}/package.json`);
const pkgRoot = upath.dirname(pkgJsonPath);
const importStatement = ol`import {${moduleName} as ${localName}} from
'${pkgRoot}/${moduleName}.mjs';`;
workboxModuleImports.push(importStatement);
}
return workboxModuleImports;
}
/**
* @param {string} pkg The workbox package that the module belongs to.
* @param {string} moduleName The name of the module to import.
* @return {string} The local variable name that corresponds to that module.
* @private
*/
getLocalName(pkg, moduleName) {
return `${pkg.replace(/-/g, '_')}_${moduleName}`;
}
/**
* @param {string} pkg The workbox package that the module belongs to.
* @param {string} moduleName The name of the module to import.
* @return {string} The local variable name that corresponds to that module.
* @private
*/
use(pkg, moduleName) {
const localName = this.getLocalName(pkg, moduleName);
this.modulesUsed.set(localName, {
moduleName,
pkg
});
return localName;
}
}
module.exports = ModuleRegistry;
@@ -0,0 +1,33 @@
"use strict";
/*
Copyright 2018 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
const errors = require('./errors');
module.exports = regexp => {
if (!(regexp instanceof RegExp)) {
throw new Error(errors['invalid-dont-cache-bust']);
}
return originalManifest => {
const manifest = originalManifest.map(entry => {
if (typeof entry.url !== 'string') {
throw new Error(errors['manifest-entry-bad-url']);
}
if (entry.url.match(regexp)) {
entry.revision = null;
}
return entry;
});
return {
manifest
};
};
};
+95
View File
@@ -0,0 +1,95 @@
"use strict";
/*
Copyright 2018 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
const template = require('lodash.template');
const swTemplate = require('../templates/sw-template');
const errors = require('./errors');
const ModuleRegistry = require('./module-registry');
const runtimeCachingConverter = require('./runtime-caching-converter');
const stringifyWithoutComments = require('./stringify-without-comments');
module.exports = ({
cacheId,
cleanupOutdatedCaches,
clientsClaim,
directoryIndex,
disableDevLogs,
ignoreURLParametersMatching,
importScripts,
manifestEntries = [],
navigateFallback,
navigateFallbackDenylist,
navigateFallbackAllowlist,
navigationPreload,
offlineGoogleAnalytics,
runtimeCaching = [],
skipWaiting
}) => {
// There needs to be at least something to precache, or else runtime caching.
if (!(manifestEntries.length > 0 || runtimeCaching.length > 0)) {
throw new Error(errors['no-manifest-entries-or-runtime-caching']);
} // These are all options that can be passed to the precacheAndRoute() method.
const precacheOptions = {
directoryIndex,
// An array of RegExp objects can't be serialized by JSON.stringify()'s
// default behavior, so if it's given, convert it manually.
ignoreURLParametersMatching: ignoreURLParametersMatching ? [] : undefined
};
let precacheOptionsString = JSON.stringify(precacheOptions, null, 2);
if (ignoreURLParametersMatching) {
precacheOptionsString = precacheOptionsString.replace(`"ignoreURLParametersMatching": []`, `"ignoreURLParametersMatching": [` + `${ignoreURLParametersMatching.join(', ')}]`);
}
let offlineAnalyticsConfigString;
if (offlineGoogleAnalytics) {
// If offlineGoogleAnalytics is a truthy value, we need to convert it to the
// format expected by the template.
offlineAnalyticsConfigString = offlineGoogleAnalytics === true ? // If it's the literal value true, then use an empty config string.
'{}' : // Otherwise, convert the config object into a more complex string, taking
// into account the fact that functions might need to be stringified.
stringifyWithoutComments(offlineGoogleAnalytics);
}
const moduleRegistry = new ModuleRegistry();
try {
const populatedTemplate = template(swTemplate)({
cacheId,
cleanupOutdatedCaches,
clientsClaim,
disableDevLogs,
importScripts,
manifestEntries,
navigateFallback,
navigateFallbackDenylist,
navigateFallbackAllowlist,
navigationPreload,
offlineAnalyticsConfigString,
precacheOptionsString,
runtimeCaching: runtimeCachingConverter(moduleRegistry, runtimeCaching),
skipWaiting,
use: moduleRegistry.use.bind(moduleRegistry)
});
const workboxImportStatements = moduleRegistry.getImportStatements(); // We need the import statements for all of the Workbox runtime modules
// prepended, so that the correct bundle can be created.
return workboxImportStatements.join('\n') + populatedTemplate;
} catch (error) {
throw new Error(`${errors['populating-sw-tmpl-failed']} '${error.message}'`);
}
};
+23
View File
@@ -0,0 +1,23 @@
"use strict";
/*
Copyright 2019 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
const upath = require('upath');
module.exports = ({
baseDirectory,
file
}) => {
// The initial path is relative to the current directory, so make it absolute.
const absolutePath = upath.resolve(file); // Convert the absolute path so that it's relative to the baseDirectory.
const relativePath = upath.relative(baseDirectory, absolutePath); // Remove any leading ./ as it won't work in a glob pattern.
const normalizedPath = upath.normalize(relativePath);
return normalizedPath;
};
+109
View File
@@ -0,0 +1,109 @@
"use strict";
/*
Copyright 2019 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
const {
SourceMapConsumer,
SourceMapGenerator
} = require('source-map');
/**
* Adapted from https://github.com/nsams/sourcemap-aware-replace, with modern
* JavaScript updates, along with additional properties copied from originalMap.
*
* @param {Object} options
* @param {string} options.jsFilename The name for the file whose contents
* correspond to originalSource.
* @param {Object} options.originalMap The sourcemap for originalSource,
* prior to any replacements.
* @param {string} options.originalSource The source code, prior to any
* replacements.
* @param {string} options.replaceString A string to swap in for searchString.
* @param {string} options.searchString A string in originalSource to replace.
* Only the first occurrence will be replaced.
* @return {{source: string, map: string}} An object containing both
* originalSource with the replacement applied, and the modified originalMap.
*
* @private
*/
async function replaceAndUpdateSourceMap({
jsFilename,
originalMap,
originalSource,
replaceString,
searchString
}) {
const generator = new SourceMapGenerator({
file: jsFilename
});
const consumer = await new SourceMapConsumer(originalMap);
let pos;
let src = originalSource;
const replacements = [];
let lineNum = 0;
let filePos = 0;
const lines = src.split('\n');
for (let line of lines) {
lineNum++;
let searchPos = 0;
while ((pos = line.indexOf(searchString, searchPos)) !== -1) {
src = src.substring(0, filePos + pos) + replaceString + src.substring(filePos + pos + searchString.length);
line = line.substring(0, pos) + replaceString + line.substring(pos + searchString.length);
replacements.push({
line: lineNum,
column: pos
});
searchPos = pos + replaceString.length;
}
filePos += line.length + 1;
}
replacements.reverse();
consumer.eachMapping(mapping => {
for (const replacement of replacements) {
if (replacement.line == mapping.generatedLine && mapping.generatedColumn > replacement.column) {
const offset = searchString.length - replaceString.length;
mapping.generatedColumn -= offset;
}
}
if (mapping.source) {
const newMapping = {
generated: {
line: mapping.generatedLine,
column: mapping.generatedColumn
},
original: {
line: mapping.originalLine,
column: mapping.originalColumn
},
source: mapping.source
};
return generator.addMapping(newMapping);
}
return mapping;
});
consumer.destroy();
const updatedSourceMap = Object.assign(JSON.parse(generator.toString()), {
names: originalMap.names,
sourceRoot: originalMap.sourceRoot,
sources: originalMap.sources,
sourcesContent: originalMap.sourcesContent
});
return {
map: JSON.stringify(updatedSourceMap),
source: src
};
}
module.exports = replaceAndUpdateSourceMap;
+150
View File
@@ -0,0 +1,150 @@
"use strict";
/*
Copyright 2018 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
const ol = require('common-tags').oneLine;
const errors = require('./errors');
const stringifyWithoutComments = require('./stringify-without-comments');
/**
* Given a set of options that configures runtime caching behavior, convert it
* to the equivalent Workbox method calls.
*
* @param {ModuleRegistry} moduleRegistry
* @param {Object} options See
* https://developers.google.com/web/tools/workbox/modules/workbox-build#generateSW-runtimeCaching
* @return {string} A JSON string representing the equivalent options.
*
* @private
*/
function getOptionsString(moduleRegistry, options = {}) {
let plugins = [];
if (options.plugins) {
// Using libs because JSON.stringify won't handle functions.
plugins = options.plugins.map(stringifyWithoutComments);
delete options.plugins;
} // Pull handler-specific config from the options object, since they are
// not directly used to construct a plugin instance. If set, need to be
// passed as options to the handler constructor instead.
const handlerOptionKeys = ['cacheName', 'networkTimeoutSeconds', 'fetchOptions', 'matchOptions'];
const handlerOptions = {};
for (const key of handlerOptionKeys) {
if (key in options) {
handlerOptions[key] = options[key];
delete options[key];
}
}
for (const [pluginName, pluginConfig] of Object.entries(options)) {
// Ensure that we have some valid configuration to pass to the plugin.
if (Object.keys(pluginConfig).length === 0) {
continue;
}
let pluginCode;
switch (pluginName) {
case 'backgroundSync':
{
const name = pluginConfig.name;
const plugin = moduleRegistry.use('workbox-background-sync', 'BackgroundSyncPlugin');
pluginCode = `new ${plugin}(${JSON.stringify(name)}`;
if ('options' in pluginConfig) {
pluginCode += `, ${stringifyWithoutComments(pluginConfig.options)}`;
}
pluginCode += `)`;
break;
}
case 'broadcastUpdate':
{
const channelName = pluginConfig.channelName;
const opts = Object.assign({
channelName
}, pluginConfig.options);
const plugin = moduleRegistry.use('workbox-broadcast-update', 'BroadcastUpdatePlugin');
pluginCode = `new ${plugin}(${stringifyWithoutComments(opts)})`;
break;
}
case 'cacheableResponse':
{
const plugin = moduleRegistry.use('workbox-cacheable-response', 'CacheableResponsePlugin');
pluginCode = `new ${plugin}(${stringifyWithoutComments(pluginConfig)})`;
break;
}
case 'expiration':
{
const plugin = moduleRegistry.use('workbox-expiration', 'ExpirationPlugin');
pluginCode = `new ${plugin}(${stringifyWithoutComments(pluginConfig)})`;
break;
}
default:
{
throw new Error(errors['bad-runtime-caching-config'] + pluginName);
}
}
plugins.push(pluginCode);
}
if (Object.keys(handlerOptions).length > 0 || plugins.length > 0) {
const optionsString = JSON.stringify(handlerOptions).slice(1, -1);
return ol`{
${optionsString ? optionsString + ',' : ''}
plugins: [${plugins.join(', ')}]
}`;
} else {
return '';
}
}
module.exports = (moduleRegistry, runtimeCaching) => {
return runtimeCaching.map(entry => {
const method = entry.method || 'GET';
if (!entry.urlPattern) {
throw new Error(errors['urlPattern-is-required']);
}
if (!entry.handler) {
throw new Error(errors['handler-is-required']);
} // This validation logic is a bit too gnarly for joi, so it's manually
// implemented here.
if (entry.options && entry.options.networkTimeoutSeconds && entry.handler !== 'NetworkFirst') {
throw new Error(errors['invalid-network-timeout-seconds']);
} // urlPattern might be a string, a RegExp object, or a function.
// If it's a string, it needs to be quoted.
const matcher = typeof entry.urlPattern === 'string' ? JSON.stringify(entry.urlPattern) : entry.urlPattern;
const registerRoute = moduleRegistry.use('workbox-routing', 'registerRoute');
if (typeof entry.handler === 'string') {
const optionsString = getOptionsString(moduleRegistry, entry.options);
const handler = moduleRegistry.use('workbox-strategies', entry.handler);
const strategyString = `new ${handler}(${optionsString})`;
return `${registerRoute}(${matcher}, ${strategyString}, '${method}');\n`;
} else if (typeof entry.handler === 'function') {
return `${registerRoute}(${matcher}, ${entry.handler}, '${method}');\n`;
}
}).filter(entry => Boolean(entry)); // Remove undefined map() return values.
};
+18
View File
@@ -0,0 +1,18 @@
"use strict";
/*
Copyright 2018 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
const objectStringify = require('stringify-object');
const stripComments = require('strip-comments');
module.exports = obj => {
return objectStringify(obj, {
transform: (_obj, _prop, str) => typeof _obj[_prop] === 'function' ? stripComments(str) : str
});
};
+144
View File
@@ -0,0 +1,144 @@
"use strict";
/*
Copyright 2018 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
const errors = require('./errors');
const additionalManifestEntriesTransform = require('./additional-manifest-entries-transform');
const maximumSizeTransform = require('./maximum-size-transform');
const modifyURLPrefixTransform = require('./modify-url-prefix-transform');
const noRevisionForURLsMatchingTransform = require('./no-revision-for-urls-matching-transform');
/**
* A `ManifestTransform` function can be used to modify the modify the `url` or
* `revision` properties of some or all of the
* {@link module:workbox-build.ManifestEntry|ManifestEntries} in the manifest.
*
* Deleting the `revision` property of an entry will cause
* the corresponding `url` to be precached without cache-busting parameters
* applied, which is to say, it implies that the URL itself contains
* proper versioning info. If the `revision` property is present, it must be
* set to a string.
*
* @example A transformation that prepended the origin of a CDN for any
* URL starting with '/assets/' could be implemented as:
*
* const cdnTransform = async (manifestEntries) => {
* const manifest = manifestEntries.map(entry => {
* const cdnOrigin = 'https://example.com';
* if (entry.url.startsWith('/assets/')) {
* entry.url = cdnOrigin + entry.url;
* }
* return entry;
* });
* return {manifest, warnings: []};
* };
*
* @example A transformation that nulls the revision field when the
* URL contains an 8-character hash surrounded by '.', indicating that it
* already contains revision information:
*
* const removeRevisionTransform = async (manifestEntries) => {
* const manifest = manifestEntries.map(entry => {
* const hashRegExp = /\.\w{8}\./;
* if (entry.url.match(hashRegExp)) {
* entry.revision = null;
* }
* return entry;
* });
* return {manifest, warnings: []};
* };
*
* @callback ManifestTransform
* @param {Array<module:workbox-build.ManifestEntry>} manifestEntries The full
* array of entries, prior to the current transformation.
* @param {Object} [compilation] When used in the webpack plugins, this param
* will be set to the current `compilation`.
* @return {Promise<module:workbox-build.ManifestTransformResult>}
* The array of entries with the transformation applied, and optionally, any
* warnings that should be reported back to the build tool.
*
* @memberof module:workbox-build
*/
module.exports = async ({
additionalManifestEntries,
dontCacheBustURLsMatching,
fileDetails,
manifestTransforms,
maximumFileSizeToCacheInBytes,
modifyURLPrefix,
transformParam
}) => {
let allWarnings = []; // Take the array of fileDetail objects and convert it into an array of
// {url, revision, size} objects, with \ replaced with /.
const normalizedManifest = fileDetails.map(fileDetails => {
return {
url: fileDetails.file.replace(/\\/g, '/'),
revision: fileDetails.hash,
size: fileDetails.size
};
});
const transformsToApply = [];
if (maximumFileSizeToCacheInBytes) {
transformsToApply.push(maximumSizeTransform(maximumFileSizeToCacheInBytes));
}
if (modifyURLPrefix) {
transformsToApply.push(modifyURLPrefixTransform(modifyURLPrefix));
}
if (dontCacheBustURLsMatching) {
transformsToApply.push(noRevisionForURLsMatchingTransform(dontCacheBustURLsMatching));
} // Run any manifestTransforms functions second-to-last.
if (manifestTransforms) {
transformsToApply.push(...manifestTransforms);
} // Run additionalManifestEntriesTransform last.
if (additionalManifestEntries) {
transformsToApply.push(additionalManifestEntriesTransform(additionalManifestEntries));
}
let transformedManifest = normalizedManifest;
for (const transform of transformsToApply) {
const result = await transform(transformedManifest, transformParam);
if (!('manifest' in result)) {
throw new Error(errors['bad-manifest-transforms-return-value']);
}
transformedManifest = result.manifest;
allWarnings = allWarnings.concat(result.warnings || []);
} // Generate some metadata about the manifest before we clear out the size
// properties from each entry.
const count = transformedManifest.length;
let size = 0;
for (const manifestEntry of transformedManifest) {
size += manifestEntry.size || 0;
delete manifestEntry.size;
}
return {
count,
size,
manifestEntries: transformedManifest,
warnings: allWarnings
};
};
+27
View File
@@ -0,0 +1,27 @@
"use strict";
/*
Copyright 2019 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
module.exports = (options, schema) => {
const {
value,
error
} = schema.validate(options, {
language: {
object: {
allowUnknown: 'is not a supported parameter.'
}
}
});
if (error) {
throw error;
}
return value;
};
@@ -0,0 +1,94 @@
"use strict";
/*
Copyright 2018 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
const fse = require('fs-extra');
const upath = require('upath');
const bundle = require('./bundle');
const errors = require('./errors');
const populateSWTemplate = require('./populate-sw-template');
module.exports = async ({
babelPresetEnvTargets,
cacheId,
cleanupOutdatedCaches,
clientsClaim,
directoryIndex,
disableDevLogs,
ignoreURLParametersMatching,
importScripts,
inlineWorkboxRuntime,
manifestEntries,
mode,
navigateFallback,
navigateFallbackDenylist,
navigateFallbackAllowlist,
navigationPreload,
offlineGoogleAnalytics,
runtimeCaching,
skipWaiting,
sourcemap,
swDest
}) => {
const outputDir = upath.dirname(swDest);
try {
await fse.mkdirp(outputDir);
} catch (error) {
throw new Error(`${errors['unable-to-make-sw-directory']}. ` + `'${error.message}'`);
}
const unbundledCode = populateSWTemplate({
cacheId,
cleanupOutdatedCaches,
clientsClaim,
directoryIndex,
disableDevLogs,
ignoreURLParametersMatching,
importScripts,
manifestEntries,
navigateFallback,
navigateFallbackDenylist,
navigateFallbackAllowlist,
navigationPreload,
offlineGoogleAnalytics,
runtimeCaching,
skipWaiting
});
try {
const files = await bundle({
babelPresetEnvTargets,
inlineWorkboxRuntime,
mode,
sourcemap,
swDest,
unbundledCode
});
const filePaths = [];
for (const file of files) {
const filePath = upath.resolve(file.name);
filePaths.push(filePath);
await fse.writeFile(filePath, file.contents);
}
return filePaths;
} catch (error) {
if (error.code === 'EISDIR') {
// See https://github.com/GoogleChrome/workbox/issues/612
throw new Error(errors['sw-write-failure-directory']);
}
throw new Error(`${errors['sw-write-failure']} '${error.message}'`);
}
};
+32
View File
@@ -0,0 +1,32 @@
"use strict";
/*
Copyright 2019 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
module.exports = {
babelPresetEnvTargets: ['chrome >= 56'],
cleanupOutdatedCaches: false,
clientsClaim: false,
compileSrc: true,
disableDevLogs: false,
exclude: [/\.map$/, /^manifest.*\.js$/],
globFollow: true,
globIgnores: ['**/node_modules/**/*'],
globPatterns: ['**/*.{js,css,html}'],
globStrict: true,
injectionPoint: 'self.__WB_MANIFEST',
inlineWorkboxRuntime: false,
maximumFileSizeToCacheInBytes: 2 * 1024 * 1024,
mode: 'production',
navigateFallback: undefined,
navigationPreload: false,
offlineGoogleAnalytics: false,
purgeOnQuotaError: true,
skipWaiting: false,
sourcemap: true,
swDestFilename: 'service-worker.js'
};
+15
View File
@@ -0,0 +1,15 @@
"use strict";
/*
Copyright 2019 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
const joi = require('@hapi/joi');
module.exports = joi.object().keys({
revision: joi.string().required().allow(null),
url: joi.string().required()
});
+12
View File
@@ -0,0 +1,12 @@
"use strict";
/*
Copyright 2019 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
const joi = require('@hapi/joi');
module.exports = joi.object().type(RegExp).error(() => 'the value must be a RegExp');
+25
View File
@@ -0,0 +1,25 @@
"use strict";
/*
Copyright 2019 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
const joi = require('@hapi/joi');
const defaults = require('../defaults');
const manifestEntryObject = require('../objects/manifest-entry');
const regExpObject = require('../objects/reg-exp');
module.exports = {
additionalManifestEntries: joi.array().items(joi.string(), manifestEntryObject),
dontCacheBustURLsMatching: regExpObject,
manifestTransforms: joi.array().items(joi.func().minArity(1).maxArity(2)),
maximumFileSizeToCacheInBytes: joi.number().min(1).default(defaults.maximumFileSizeToCacheInBytes),
mode: joi.string().default(process.env.NODE_ENV || defaults.mode),
modifyURLPrefix: joi.object()
};
+67
View File
@@ -0,0 +1,67 @@
"use strict";
/*
Copyright 2019 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
const joi = require('@hapi/joi');
const defaults = require('../defaults');
const regExpObject = require('../objects/reg-exp');
module.exports = {
babelPresetEnvTargets: joi.array().items(joi.string()).default(defaults.babelPresetEnvTargets),
cacheId: joi.string(),
cleanupOutdatedCaches: joi.boolean().default(defaults.cleanupOutdatedCaches),
clientsClaim: joi.boolean().default(defaults.clientsClaim),
directoryIndex: joi.string(),
disableDevLogs: joi.boolean().default(defaults.disableDevLogs),
ignoreURLParametersMatching: joi.array().items(regExpObject),
importScripts: joi.array().items(joi.string()),
inlineWorkboxRuntime: joi.boolean().default(defaults.inlineWorkboxRuntime),
navigateFallback: joi.string().default(defaults.navigateFallback),
navigateFallbackAllowlist: joi.array().items(regExpObject),
navigateFallbackBlacklist: joi.forbidden().error(new Error('navigateFallbackBlacklist has been renamed navigateFallbackDenylist.')),
navigateFallbackDenylist: joi.array().items(regExpObject),
navigateFallbackWhitelist: joi.forbidden().error(new Error('navigateFallbackWhitelist has been renamed navigateFallbackAllowlist.')),
navigationPreload: joi.boolean().default(defaults.navigationPreload),
offlineGoogleAnalytics: joi.alternatives().try(joi.boolean(), joi.object()).default(defaults.offlineGoogleAnalytics),
runtimeCaching: joi.array().items(joi.object().keys({
method: joi.string().valid('DELETE', 'GET', 'HEAD', 'PATCH', 'POST', 'PUT'),
urlPattern: [regExpObject, joi.string(), joi.func()],
handler: [joi.func(), joi.string().valid('CacheFirst', 'CacheOnly', 'NetworkFirst', 'NetworkOnly', 'StaleWhileRevalidate')],
options: joi.object().keys({
backgroundSync: joi.object().keys({
name: joi.string().required(),
options: joi.object()
}),
broadcastUpdate: joi.object().keys({
channelName: joi.string().required(),
options: joi.object()
}),
cacheableResponse: joi.object().keys({
statuses: joi.array().items(joi.number().min(0).max(599)),
headers: joi.object()
}).or('statuses', 'headers'),
cacheName: joi.string(),
expiration: joi.object().keys({
maxEntries: joi.number().min(1),
maxAgeSeconds: joi.number().min(1),
purgeOnQuotaError: joi.boolean().default(defaults.purgeOnQuotaError)
}).or('maxEntries', 'maxAgeSeconds'),
networkTimeoutSeconds: joi.number().min(1),
plugins: joi.array().items(joi.object()),
fetchOptions: joi.object(),
matchOptions: joi.object()
}).with('expiration', 'cacheName')
}).requiredKeys('urlPattern', 'handler')).when('navigationPreload', {
is: true,
then: joi.required()
}),
skipWaiting: joi.boolean().default(defaults.skipWaiting),
sourcemap: joi.boolean().default(defaults.sourcemap)
};
+23
View File
@@ -0,0 +1,23 @@
"use strict";
/*
Copyright 2019 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
const joi = require('@hapi/joi');
const defaults = require('../defaults');
module.exports = {
globDirectory: joi.string(),
globFollow: joi.boolean().default(defaults.globFollow),
globIgnores: joi.array().items(joi.string()).default(defaults.globIgnores),
globPatterns: joi.array().items(joi.string()).default(defaults.globPatterns),
globStrict: joi.boolean().default(defaults.globStrict),
// templatedURLs is an object where any property name is valid, and the values
// can be either a string or an array of strings.
templatedURLs: joi.object().pattern(/./, [joi.string(), joi.array().items(joi.string())])
};
+17
View File
@@ -0,0 +1,17 @@
"use strict";
/*
Copyright 2019 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
const joi = require('@hapi/joi');
const defaults = require('../defaults');
module.exports = {
injectionPoint: joi.string().default(defaults.injectionPoint),
swSrc: joi.string().required()
};
+21
View File
@@ -0,0 +1,21 @@
"use strict";
/*
Copyright 2019 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
const joi = require('@hapi/joi');
const defaults = require('../defaults');
const regExpObject = require('../objects/reg-exp');
module.exports = {
chunks: joi.array().items(joi.string()),
exclude: joi.array().items(joi.string(), regExpObject, joi.func().arity(1)).default(defaults.exclude),
excludeChunks: joi.array().items(joi.string()),
include: joi.array().items(joi.string(), regExpObject, joi.func().arity(1))
};
+21
View File
@@ -0,0 +1,21 @@
"use strict";
/*
Copyright 2019 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
const joi = require('@hapi/joi');
const basePartial = require('../partials/base');
const generatePartial = require('../partials/generate');
const globPartial = require('../partials/glob');
const supportedOptions = Object.assign({
swDest: joi.string().required().regex(/\.js$/)
}, basePartial, generatePartial, globPartial);
module.exports = joi.object().keys(supportedOptions);
+17
View File
@@ -0,0 +1,17 @@
"use strict";
/*
Copyright 2019 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
const joi = require('@hapi/joi');
const basePartial = require('../partials/base');
const globPartial = require('../partials/glob');
const supportedOptions = Object.assign({}, basePartial, globPartial);
module.exports = joi.object().keys(supportedOptions);
+21
View File
@@ -0,0 +1,21 @@
"use strict";
/*
Copyright 2019 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
const joi = require('@hapi/joi');
const basePartial = require('../partials/base');
const globPartial = require('../partials/glob');
const injectPartial = require('../partials/inject');
const supportedOptions = Object.assign({
swDest: joi.string().required().regex(/\.js$/)
}, basePartial, globPartial, injectPartial);
module.exports = joi.object().keys(supportedOptions);
+24
View File
@@ -0,0 +1,24 @@
"use strict";
/*
Copyright 2019 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
const joi = require('@hapi/joi');
const basePartial = require('../partials/base');
const defaults = require('../defaults');
const generatePartial = require('../partials/generate');
const webpackPartial = require('../partials/webpack');
const supportedOptions = Object.assign({
importScriptsViaChunks: joi.array().items(joi.string()),
swDest: joi.string().default(defaults.swDestFilename)
}, basePartial, generatePartial, webpackPartial);
module.exports = joi.object().keys(supportedOptions);
@@ -0,0 +1,42 @@
"use strict";
/*
Copyright 2019 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
const joi = require('@hapi/joi');
const upath = require('upath');
const basePartial = require('../partials/base');
const defaults = require('../defaults');
const injectPartial = require('../partials/inject');
const webpackPartial = require('../partials/webpack'); // See https://github.com/hapijs/joi/blob/v16.0.0-rc2/API.md#anydefaultvalue-description
const swSrcBasename = context => {
const {
name
} = upath.parse(context.swSrc); // Always use the .js extension when generating a default filename.
return name + '.js';
};
swSrcBasename.description = 'derived from the swSrc file name';
const supportedOptions = Object.assign({
compileSrc: joi.boolean().default(defaults.compileSrc),
webpackCompilationPlugins: joi.array().items(joi.object()).when('compileSrc', {
is: false,
then: joi.forbidden()
})
}, basePartial, injectPartial, webpackPartial);
module.exports = joi.object().keys(supportedOptions).keys({
// List this separately, so that the swSrc validation happens first.
swDest: joi.string().default(swSrcBasename)
});
+61
View File
@@ -0,0 +1,61 @@
"use strict";
/*
Copyright 2018 Google LLC
Use of this source code is governed by an MIT-style
license that can be found in the LICENSE file or at
https://opensource.org/licenses/MIT.
*/
module.exports = `/**
* Welcome to your Workbox-powered service worker!
*
* You'll need to register this file in your web app.
* See https://goo.gl/nhQhGp
*
* The rest of the code is auto-generated. Please don't update this file
* directly; instead, make changes to your Workbox build configuration
* and re-run your build process.
* See https://goo.gl/2aRDsh
*/
<% if (importScripts) { %>
importScripts(
<%= importScripts.map(JSON.stringify).join(',\\n ') %>
);
<% } %>
<% if (navigationPreload) { %><%= use('workbox-navigation-preload', 'enable') %>();<% } %>
<% if (cacheId) { %><%= use('workbox-core', 'setCacheNameDetails') %>({prefix: <%= JSON.stringify(cacheId) %>});<% } %>
<% if (skipWaiting) { %>
<%= use('workbox-core', 'skipWaiting') %>();
<% } else { %>
self.addEventListener('message', (event) => {
if (event.data && event.data.type === 'SKIP_WAITING') {
self.skipWaiting();
}
});
<% } %>
<% if (clientsClaim) { %><%= use('workbox-core', 'clientsClaim') %>();<% } %>
<% if (Array.isArray(manifestEntries) && manifestEntries.length > 0) {%>
/**
* The precacheAndRoute() method efficiently caches and responds to
* requests for URLs in the manifest.
* See https://goo.gl/S9QRab
*/
<%= use('workbox-precaching', 'precacheAndRoute') %>(<%= JSON.stringify(manifestEntries, null, 2) %>, <%= precacheOptionsString %>);
<% if (cleanupOutdatedCaches) { %><%= use('workbox-precaching', 'cleanupOutdatedCaches') %>();<% } %>
<% if (navigateFallback) { %><%= use('workbox-routing', 'registerRoute') %>(new <%= use('workbox-routing', 'NavigationRoute') %>(<%= use('workbox-precaching', 'createHandlerBoundToURL') %>(<%= JSON.stringify(navigateFallback) %>)<% if (navigateFallbackAllowlist || navigateFallbackDenylist) { %>, {
<% if (navigateFallbackAllowlist) { %>allowlist: [<%= navigateFallbackAllowlist %>],<% } %>
<% if (navigateFallbackDenylist) { %>denylist: [<%= navigateFallbackDenylist %>],<% } %>
}<% } %>));<% } %>
<% } %>
<% if (runtimeCaching) { runtimeCaching.forEach(runtimeCachingString => {%><%= runtimeCachingString %><% });} %>
<% if (offlineAnalyticsConfigString) { %><%= use('workbox-google-analytics', 'initialize') %>(<%= offlineAnalyticsConfigString %>);<% } %>
<% if (disableDevLogs) { %>self.__WB_DISABLE_DEV_LOGS = true;<% } %>`;
+864
View File
@@ -0,0 +1,864 @@
8.1.0 / 2019-06-28
------------------
- Add support for promisified `fs.realpath.native` in Node v9.2+ ([#650](https://github.com/jprichardson/node-fs-extra/issues/650), [#682](https://github.com/jprichardson/node-fs-extra/pull/682))
- Update `graceful-fs` dependency ([#700](https://github.com/jprichardson/node-fs-extra/pull/700))
- Use `graceful-fs` everywhere ([#700](https://github.com/jprichardson/node-fs-extra/pull/700))
8.0.1 / 2019-05-13
------------------
- Fix bug `Maximum call stack size exceeded` error in `util/stat` ([#679](https://github.com/jprichardson/node-fs-extra/pull/679))
8.0.0 / 2019-05-11
------------------
**NOTE:** Node.js v6 support is deprecated, and will be dropped in the next major release.
- Use `renameSync()` under the hood in `moveSync()`
- Fix bug with bind-mounted directories in `copy*()` ([#613](https://github.com/jprichardson/node-fs-extra/issues/613), [#618](https://github.com/jprichardson/node-fs-extra/pull/618))
- Fix bug in `move()` with case-insensitive file systems
- Use `fs.stat()`'s `bigint` option in `copy*()` & `move*()` where possible ([#657](https://github.com/jprichardson/node-fs-extra/issues/657))
7.0.1 / 2018-11-07
------------------
- Fix `removeSync()` on Windows, in some cases, it would error out with `ENOTEMPTY` ([#646](https://github.com/jprichardson/node-fs-extra/pull/646))
- Document `mode` option for `ensureDir*()` ([#587](https://github.com/jprichardson/node-fs-extra/pull/587))
- Don't include documentation files in npm package tarball ([#642](https://github.com/jprichardson/node-fs-extra/issues/642), [#643](https://github.com/jprichardson/node-fs-extra/pull/643))
7.0.0 / 2018-07-16
------------------
- **BREAKING:** Refine `copy*()` handling of symlinks to properly detect symlinks that point to the same file. ([#582](https://github.com/jprichardson/node-fs-extra/pull/582))
- Fix bug with copying write-protected directories ([#600](https://github.com/jprichardson/node-fs-extra/pull/600))
- Universalify `fs.lchmod()` ([#596](https://github.com/jprichardson/node-fs-extra/pull/596))
- Add `engines` field to `package.json` ([#580](https://github.com/jprichardson/node-fs-extra/pull/580))
6.0.1 / 2018-05-09
------------------
- Fix `fs.promises` `ExperimentalWarning` on Node v10.1.0 ([#578](https://github.com/jprichardson/node-fs-extra/pull/578))
6.0.0 / 2018-05-01
------------------
- Drop support for Node.js versions 4, 5, & 7 ([#564](https://github.com/jprichardson/node-fs-extra/pull/564))
- Rewrite `move` to use `fs.rename` where possible ([#549](https://github.com/jprichardson/node-fs-extra/pull/549))
- Don't convert relative paths to absolute paths for `filter` ([#554](https://github.com/jprichardson/node-fs-extra/pull/554))
- `copy*`'s behavior when `preserveTimestamps` is `false` has been OS-dependent since 5.0.0, but that's now explicitly noted in the docs ([#563](https://github.com/jprichardson/node-fs-extra/pull/563))
- Fix subdirectory detection for `copy*` & `move*` ([#541](https://github.com/jprichardson/node-fs-extra/pull/541))
- Handle case-insensitive paths correctly in `copy*` ([#568](https://github.com/jprichardson/node-fs-extra/pull/568))
5.0.0 / 2017-12-11
------------------
Significant refactor of `copy()` & `copySync()`, including breaking changes. No changes to other functions in this release.
Huge thanks to **[@manidlou](https://github.com/manidlou)** for doing most of the work on this release.
- The `filter` option can no longer be a RegExp (must be a function). This was deprecated since fs-extra v1.0.0. [#512](https://github.com/jprichardson/node-fs-extra/pull/512)
- `copy()`'s `filter` option can now be a function that returns a Promise. [#518](https://github.com/jprichardson/node-fs-extra/pull/518)
- `copy()` & `copySync()` now use `fs.copyFile()`/`fs.copyFileSync()` in environments that support it (currently Node 8.5.0+). Older Node versions still get the old implementation. [#505](https://github.com/jprichardson/node-fs-extra/pull/505)
- Don't allow copying a directory into itself. [#83](https://github.com/jprichardson/node-fs-extra/issues/83)
- Handle copying between identical files. [#198](https://github.com/jprichardson/node-fs-extra/issues/198)
- Error out when copying an empty folder to a path that already exists. [#464](https://github.com/jprichardson/node-fs-extra/issues/464)
- Don't create `dest`'s parent if the `filter` function aborts the `copy()` operation. [#517](https://github.com/jprichardson/node-fs-extra/pull/517)
- Fix `writeStream` not being closed if there was an error in `copy()`. [#516](https://github.com/jprichardson/node-fs-extra/pull/516)
4.0.3 / 2017-12-05
------------------
- Fix wrong `chmod` values in `fs.remove()` [#501](https://github.com/jprichardson/node-fs-extra/pull/501)
- Fix `TypeError` on systems that don't have some `fs` operations like `lchown` [#520](https://github.com/jprichardson/node-fs-extra/pull/520)
4.0.2 / 2017-09-12
------------------
- Added `EOL` option to `writeJson*` & `outputJson*` (via upgrade to jsonfile v4)
- Added promise support to [`fs.copyFile()`](https://nodejs.org/api/fs.html#fs_fs_copyfile_src_dest_flags_callback) in Node 8.5+
- Added `.js` extension to `main` field in `package.json` for better tooling compatibility. [#485](https://github.com/jprichardson/node-fs-extra/pull/485)
4.0.1 / 2017-07-31
------------------
### Fixed
- Previously, `ensureFile()` & `ensureFileSync()` would do nothing if the path was a directory. Now, they error out for consistency with `ensureDir()`. [#465](https://github.com/jprichardson/node-fs-extra/issues/465), [#466](https://github.com/jprichardson/node-fs-extra/pull/466), [#470](https://github.com/jprichardson/node-fs-extra/issues/470)
4.0.0 / 2017-07-14
------------------
### Changed
- **BREAKING:** The promisified versions of `fs.read()` & `fs.write()` now return objects. See [the docs](docs/fs-read-write.md) for details. [#436](https://github.com/jprichardson/node-fs-extra/issues/436), [#449](https://github.com/jprichardson/node-fs-extra/pull/449)
- `fs.move()` now errors out when destination is a subdirectory of source. [#458](https://github.com/jprichardson/node-fs-extra/pull/458)
- Applied upstream fixes from `rimraf` to `fs.remove()` & `fs.removeSync()`. [#459](https://github.com/jprichardson/node-fs-extra/pull/459)
### Fixed
- Got `fs.outputJSONSync()` working again; it was broken due to refactoring. [#428](https://github.com/jprichardson/node-fs-extra/pull/428)
Also clarified the docs in a few places.
3.0.1 / 2017-05-04
------------------
- Fix bug in `move()` & `moveSync()` when source and destination are the same, and source does not exist. [#415](https://github.com/jprichardson/node-fs-extra/pull/415)
3.0.0 / 2017-04-27
------------------
### Added
- **BREAKING:** Added Promise support. All asynchronous native fs methods and fs-extra methods now return a promise if the callback is not passed. [#403](https://github.com/jprichardson/node-fs-extra/pull/403)
- `pathExists()`, a replacement for the deprecated `fs.exists`. `pathExists` has a normal error-first callback signature. Also added `pathExistsSync`, an alias to `fs.existsSync`, for completeness. [#406](https://github.com/jprichardson/node-fs-extra/pull/406)
### Removed
- **BREAKING:** Removed support for setting the default spaces for `writeJson()`, `writeJsonSync()`, `outputJson()`, & `outputJsonSync()`. This was undocumented. [#402](https://github.com/jprichardson/node-fs-extra/pull/402)
### Changed
- Upgraded jsonfile dependency to v3.0.0:
- **BREAKING:** Changed behavior of `throws` option for `readJsonSync()`; now does not throw filesystem errors when `throws` is `false`.
- **BREAKING:** `writeJson()`, `writeJsonSync()`, `outputJson()`, & `outputJsonSync()` now output minified JSON by default for consistency with `JSON.stringify()`; set the `spaces` option to `2` to override this new behavior. [#402](https://github.com/jprichardson/node-fs-extra/pull/402)
- Use `Buffer.allocUnsafe()` instead of `new Buffer()` in environments that support it. [#394](https://github.com/jprichardson/node-fs-extra/pull/394)
### Fixed
- `removeSync()` silently failed on Windows in some cases. Now throws an `EBUSY` error. [#408](https://github.com/jprichardson/node-fs-extra/pull/408)
2.1.2 / 2017-03-16
------------------
### Fixed
- Weird windows bug that resulted in `ensureDir()`'s callback being called twice in some cases. This bug may have also affected `remove()`. See [#392](https://github.com/jprichardson/node-fs-extra/issues/392), [#393](https://github.com/jprichardson/node-fs-extra/pull/393)
2.1.1 / 2017-03-15
------------------
### Fixed
- Reverted [`5597bd`](https://github.com/jprichardson/node-fs-extra/commit/5597bd5b67f7d060f5f5bf26e9635be48330f5d7), this broke compatibility with Node.js versions v4+ but less than `v4.5.0`.
- Remove `Buffer.alloc()` usage in `moveSync()`.
2.1.0 / 2017-03-15
------------------
Thanks to [Mani Maghsoudlou (@manidlou)](https://github.com/manidlou) & [Jan Peer Stöcklmair (@JPeer264)](https://github.com/JPeer264) for their extraordinary help with this release!
### Added
- `moveSync()` See [#309], [#381](https://github.com/jprichardson/node-fs-extra/pull/381). ([@manidlou](https://github.com/manidlou))
- `copy()` and `copySync()`'s `filter` option now gets the destination path passed as the second parameter. [#366](https://github.com/jprichardson/node-fs-extra/pull/366) ([@manidlou](https://github.com/manidlou))
### Changed
- Use `Buffer.alloc()` instead of deprecated `new Buffer()` in `copySync()`. [#380](https://github.com/jprichardson/node-fs-extra/pull/380) ([@manidlou](https://github.com/manidlou))
- Refactored entire codebase to use ES6 features supported by Node.js v4+ [#355](https://github.com/jprichardson/node-fs-extra/issues/355). [(@JPeer264)](https://github.com/JPeer264)
- Refactored docs. ([@manidlou](https://github.com/manidlou))
### Fixed
- `move()` shouldn't error out when source and dest are the same. [#377](https://github.com/jprichardson/node-fs-extra/issues/377), [#378](https://github.com/jprichardson/node-fs-extra/pull/378) ([@jdalton](https://github.com/jdalton))
2.0.0 / 2017-01-16
------------------
### Removed
- **BREAKING:** Removed support for Node `v0.12`. The Node foundation stopped officially supporting it
on Jan 1st, 2017.
- **BREAKING:** Remove `walk()` and `walkSync()`. `walkSync()` was only part of `fs-extra` for a little
over two months. Use [klaw](https://github.com/jprichardson/node-klaw) instead of `walk()`, in fact, `walk()` was just
an alias to klaw. For `walkSync()` use [klaw-sync](https://github.com/mawni/node-klaw-sync). See: [#338], [#339]
### Changed
- **BREAKING:** Renamed `clobber` to `overwrite`. This affects `copy()`, `copySync()`, and `move()`. [#330], [#333]
- Moved docs, to `docs/`. [#340]
### Fixed
- Apply filters to directories in `copySync()` like in `copy()`. [#324]
- A specific condition when disk is under heavy use, `copy()` can fail. [#326]
1.0.0 / 2016-11-01
------------------
After five years of development, we finally have reach the 1.0.0 milestone! Big thanks goes
to [Ryan Zim](https://github.com/RyanZim) for leading the charge on this release!
### Added
- `walkSync()`
### Changed
- **BREAKING**: dropped Node v0.10 support.
- disabled `rimaf` globbing, wasn't used. [#280]
- deprecate `copy()/copySync()` option `filter` if it's a `RegExp`. `filter` should now be a function.
- inline `rimraf`. This is temporary and was done because `rimraf` depended upon the beefy `glob` which `fs-extra` does not use. [#300]
### Fixed
- bug fix proper closing of file handle on `utimesMillis()` [#271]
- proper escaping of files with dollar signs [#291]
- `copySync()` failed if user didn't own file. [#199], [#301]
0.30.0 / 2016-04-28
-------------------
- Brought back Node v0.10 support. I didn't realize there was still demand. Official support will end **2016-10-01**.
0.29.0 / 2016-04-27
-------------------
- **BREAKING**: removed support for Node v0.10. If you still want to use Node v0.10, everything should work except for `ensureLink()/ensureSymlink()`. Node v0.12 is still supported but will be dropped in the near future as well.
0.28.0 / 2016-04-17
-------------------
- **BREAKING**: removed `createOutputStream()`. Use https://www.npmjs.com/package/create-output-stream. See: [#192][#192]
- `mkdirs()/mkdirsSync()` check for invalid win32 path chars. See: [#209][#209], [#237][#237]
- `mkdirs()/mkdirsSync()` if drive not mounted, error. See: [#93][#93]
0.27.0 / 2016-04-15
-------------------
- add `dereference` option to `copySync()`. [#235][#235]
0.26.7 / 2016-03-16
-------------------
- fixed `copy()` if source and dest are the same. [#230][#230]
0.26.6 / 2016-03-15
-------------------
- fixed if `emptyDir()` does not have a callback: [#229][#229]
0.26.5 / 2016-01-27
-------------------
- `copy()` with two arguments (w/o callback) was broken. See: [#215][#215]
0.26.4 / 2016-01-05
-------------------
- `copySync()` made `preserveTimestamps` default consistent with `copy()` which is `false`. See: [#208][#208]
0.26.3 / 2015-12-17
-------------------
- fixed `copy()` hangup in copying blockDevice / characterDevice / `/dev/null`. See: [#193][#193]
0.26.2 / 2015-11-02
-------------------
- fixed `outputJson{Sync}()` spacing adherence to `fs.spaces`
0.26.1 / 2015-11-02
-------------------
- fixed `copySync()` when `clogger=true` and the destination is read only. See: [#190][#190]
0.26.0 / 2015-10-25
-------------------
- extracted the `walk()` function into its own module [`klaw`](https://github.com/jprichardson/node-klaw).
0.25.0 / 2015-10-24
-------------------
- now has a file walker `walk()`
0.24.0 / 2015-08-28
-------------------
- removed alias `delete()` and `deleteSync()`. See: [#171][#171]
0.23.1 / 2015-08-07
-------------------
- Better handling of errors for `move()` when moving across devices. [#170][#170]
- `ensureSymlink()` and `ensureLink()` should not throw errors if link exists. [#169][#169]
0.23.0 / 2015-08-06
-------------------
- added `ensureLink{Sync}()` and `ensureSymlink{Sync}()`. See: [#165][#165]
0.22.1 / 2015-07-09
-------------------
- Prevent calling `hasMillisResSync()` on module load. See: [#149][#149].
Fixes regression that was introduced in `0.21.0`.
0.22.0 / 2015-07-09
-------------------
- preserve permissions / ownership in `copy()`. See: [#54][#54]
0.21.0 / 2015-07-04
-------------------
- add option to preserve timestamps in `copy()` and `copySync()`. See: [#141][#141]
- updated `graceful-fs@3.x` to `4.x`. This brings in features from `amazing-graceful-fs` (much cleaner code / less hacks)
0.20.1 / 2015-06-23
-------------------
- fixed regression caused by latest jsonfile update: See: https://github.com/jprichardson/node-jsonfile/issues/26
0.20.0 / 2015-06-19
-------------------
- removed `jsonfile` aliases with `File` in the name, they weren't documented and probably weren't in use e.g.
this package had both `fs.readJsonFile` and `fs.readJson` that were aliases to each other, now use `fs.readJson`.
- preliminary walker created. Intentionally not documented. If you use it, it will almost certainly change and break your code.
- started moving tests inline
- upgraded to `jsonfile@2.1.0`, can now pass JSON revivers/replacers to `readJson()`, `writeJson()`, `outputJson()`
0.19.0 / 2015-06-08
-------------------
- `fs.copy()` had support for Node v0.8, dropped support
0.18.4 / 2015-05-22
-------------------
- fixed license field according to this: [#136][#136] and https://github.com/npm/npm/releases/tag/v2.10.0
0.18.3 / 2015-05-08
-------------------
- bugfix: handle `EEXIST` when clobbering on some Linux systems. [#134][#134]
0.18.2 / 2015-04-17
-------------------
- bugfix: allow `F_OK` ([#120][#120])
0.18.1 / 2015-04-15
-------------------
- improved windows support for `move()` a bit. https://github.com/jprichardson/node-fs-extra/commit/92838980f25dc2ee4ec46b43ee14d3c4a1d30c1b
- fixed a lot of tests for Windows (appveyor)
0.18.0 / 2015-03-31
-------------------
- added `emptyDir()` and `emptyDirSync()`
0.17.0 / 2015-03-28
-------------------
- `copySync` added `clobber` option (before always would clobber, now if `clobber` is `false` it throws an error if the destination exists).
**Only works with files at the moment.**
- `createOutputStream()` added. See: [#118][#118]
0.16.5 / 2015-03-08
-------------------
- fixed `fs.move` when `clobber` is `true` and destination is a directory, it should clobber. [#114][#114]
0.16.4 / 2015-03-01
-------------------
- `fs.mkdirs` fix infinite loop on Windows. See: See https://github.com/substack/node-mkdirp/pull/74 and https://github.com/substack/node-mkdirp/issues/66
0.16.3 / 2015-01-28
-------------------
- reverted https://github.com/jprichardson/node-fs-extra/commit/1ee77c8a805eba5b99382a2591ff99667847c9c9
0.16.2 / 2015-01-28
-------------------
- fixed `fs.copy` for Node v0.8 (support is temporary and will be removed in the near future)
0.16.1 / 2015-01-28
-------------------
- if `setImmediate` is not available, fall back to `process.nextTick`
0.16.0 / 2015-01-28
-------------------
- bugfix `fs.move()` into itself. Closes [#104]
- bugfix `fs.move()` moving directory across device. Closes [#108]
- added coveralls support
- bugfix: nasty multiple callback `fs.copy()` bug. Closes [#98]
- misc fs.copy code cleanups
0.15.0 / 2015-01-21
-------------------
- dropped `ncp`, imported code in
- because of previous, now supports `io.js`
- `graceful-fs` is now a dependency
0.14.0 / 2015-01-05
-------------------
- changed `copy`/`copySync` from `fs.copy(src, dest, [filters], callback)` to `fs.copy(src, dest, [options], callback)` [#100][#100]
- removed mockfs tests for mkdirp (this may be temporary, but was getting in the way of other tests)
0.13.0 / 2014-12-10
-------------------
- removed `touch` and `touchSync` methods (they didn't handle permissions like UNIX touch)
- updated `"ncp": "^0.6.0"` to `"ncp": "^1.0.1"`
- imported `mkdirp` => `minimist` and `mkdirp` are no longer dependences, should now appease people who wanted `mkdirp` to be `--use_strict` safe. See [#59]([#59][#59])
0.12.0 / 2014-09-22
-------------------
- copy symlinks in `copySync()` [#85][#85]
0.11.1 / 2014-09-02
-------------------
- bugfix `copySync()` preserve file permissions [#80][#80]
0.11.0 / 2014-08-11
-------------------
- upgraded `"ncp": "^0.5.1"` to `"ncp": "^0.6.0"`
- upgrade `jsonfile": "^1.2.0"` to `jsonfile": "^2.0.0"` => on write, json files now have `\n` at end. Also adds `options.throws` to `readJsonSync()`
see https://github.com/jprichardson/node-jsonfile#readfilesyncfilename-options for more details.
0.10.0 / 2014-06-29
------------------
* bugfix: upgaded `"jsonfile": "~1.1.0"` to `"jsonfile": "^1.2.0"`, bumped minor because of `jsonfile` dep change
from `~` to `^`. [#67]
0.9.1 / 2014-05-22
------------------
* removed Node.js `0.8.x` support, `0.9.0` was published moments ago and should have been done there
0.9.0 / 2014-05-22
------------------
* upgraded `ncp` from `~0.4.2` to `^0.5.1`, [#58]
* upgraded `rimraf` from `~2.2.6` to `^2.2.8`
* upgraded `mkdirp` from `0.3.x` to `^0.5.0`
* added methods `ensureFile()`, `ensureFileSync()`
* added methods `ensureDir()`, `ensureDirSync()` [#31]
* added `move()` method. From: https://github.com/andrewrk/node-mv
0.8.1 / 2013-10-24
------------------
* copy failed to return an error to the callback if a file doesn't exist (ulikoehler [#38], [#39])
0.8.0 / 2013-10-14
------------------
* `filter` implemented on `copy()` and `copySync()`. (Srirangan / [#36])
0.7.1 / 2013-10-12
------------------
* `copySync()` implemented (Srirangan / [#33])
* updated to the latest `jsonfile` version `1.1.0` which gives `options` params for the JSON methods. Closes [#32]
0.7.0 / 2013-10-07
------------------
* update readme conventions
* `copy()` now works if destination directory does not exist. Closes [#29]
0.6.4 / 2013-09-05
------------------
* changed `homepage` field in package.json to remove NPM warning
0.6.3 / 2013-06-28
------------------
* changed JSON spacing default from `4` to `2` to follow Node conventions
* updated `jsonfile` dep
* updated `rimraf` dep
0.6.2 / 2013-06-28
------------------
* added .npmignore, [#25]
0.6.1 / 2013-05-14
------------------
* modified for `strict` mode, closes [#24]
* added `outputJson()/outputJsonSync()`, closes [#23]
0.6.0 / 2013-03-18
------------------
* removed node 0.6 support
* added node 0.10 support
* upgraded to latest `ncp` and `rimraf`.
* optional `graceful-fs` support. Closes [#17]
0.5.0 / 2013-02-03
------------------
* Removed `readTextFile`.
* Renamed `readJSONFile` to `readJSON` and `readJson`, same with write.
* Restructured documentation a bit. Added roadmap.
0.4.0 / 2013-01-28
------------------
* Set default spaces in `jsonfile` from 4 to 2.
* Updated `testutil` deps for tests.
* Renamed `touch()` to `createFile()`
* Added `outputFile()` and `outputFileSync()`
* Changed creation of testing diretories so the /tmp dir is not littered.
* Added `readTextFile()` and `readTextFileSync()`.
0.3.2 / 2012-11-01
------------------
* Added `touch()` and `touchSync()` methods.
0.3.1 / 2012-10-11
------------------
* Fixed some stray globals.
0.3.0 / 2012-10-09
------------------
* Removed all CoffeeScript from tests.
* Renamed `mkdir` to `mkdirs`/`mkdirp`.
0.2.1 / 2012-09-11
------------------
* Updated `rimraf` dep.
0.2.0 / 2012-09-10
------------------
* Rewrote module into JavaScript. (Must still rewrite tests into JavaScript)
* Added all methods of [jsonfile](https://github.com/jprichardson/node-jsonfile)
* Added Travis-CI.
0.1.3 / 2012-08-13
------------------
* Added method `readJSONFile`.
0.1.2 / 2012-06-15
------------------
* Bug fix: `deleteSync()` didn't exist.
* Verified Node v0.8 compatibility.
0.1.1 / 2012-06-15
------------------
* Fixed bug in `remove()`/`delete()` that wouldn't execute the function if a callback wasn't passed.
0.1.0 / 2012-05-31
------------------
* Renamed `copyFile()` to `copy()`. `copy()` can now copy directories (recursively) too.
* Renamed `rmrf()` to `remove()`.
* `remove()` aliased with `delete()`.
* Added `mkdirp` capabilities. Named: `mkdir()`. Hides Node.js native `mkdir()`.
* Instead of exporting the native `fs` module with new functions, I now copy over the native methods to a new object and export that instead.
0.0.4 / 2012-03-14
------------------
* Removed CoffeeScript dependency
0.0.3 / 2012-01-11
------------------
* Added methods rmrf and rmrfSync
* Moved tests from Jasmine to Mocha
[#344]: https://github.com/jprichardson/node-fs-extra/issues/344 "Licence Year"
[#343]: https://github.com/jprichardson/node-fs-extra/pull/343 "Add klaw-sync link to readme"
[#342]: https://github.com/jprichardson/node-fs-extra/pull/342 "allow preserveTimestamps when use move"
[#341]: https://github.com/jprichardson/node-fs-extra/issues/341 "mkdirp(path.dirname(dest) in move() logic needs cleaning up [question]"
[#340]: https://github.com/jprichardson/node-fs-extra/pull/340 "Move docs to seperate docs folder [documentation]"
[#339]: https://github.com/jprichardson/node-fs-extra/pull/339 "Remove walk() & walkSync() [feature-walk]"
[#338]: https://github.com/jprichardson/node-fs-extra/issues/338 "Remove walk() and walkSync() [feature-walk]"
[#337]: https://github.com/jprichardson/node-fs-extra/issues/337 "copy doesn't return a yieldable value"
[#336]: https://github.com/jprichardson/node-fs-extra/pull/336 "Docs enhanced walk sync [documentation, feature-walk]"
[#335]: https://github.com/jprichardson/node-fs-extra/pull/335 "Refactor move() tests [feature-move]"
[#334]: https://github.com/jprichardson/node-fs-extra/pull/334 "Cleanup lib/move/index.js [feature-move]"
[#333]: https://github.com/jprichardson/node-fs-extra/pull/333 "Rename clobber to overwrite [feature-copy, feature-move]"
[#332]: https://github.com/jprichardson/node-fs-extra/pull/332 "BREAKING: Drop Node v0.12 & io.js support"
[#331]: https://github.com/jprichardson/node-fs-extra/issues/331 "Add support for chmodr [enhancement, future]"
[#330]: https://github.com/jprichardson/node-fs-extra/pull/330 "BREAKING: Do not error when copy destination exists & clobber: false [feature-copy]"
[#329]: https://github.com/jprichardson/node-fs-extra/issues/329 "Does .walk() scale to large directories? [question]"
[#328]: https://github.com/jprichardson/node-fs-extra/issues/328 "Copying files corrupts [feature-copy, needs-confirmed]"
[#327]: https://github.com/jprichardson/node-fs-extra/pull/327 "Use writeStream 'finish' event instead of 'close' [bug, feature-copy]"
[#326]: https://github.com/jprichardson/node-fs-extra/issues/326 "fs.copy fails with chmod error when disk under heavy use [bug, feature-copy]"
[#325]: https://github.com/jprichardson/node-fs-extra/issues/325 "ensureDir is difficult to promisify [enhancement]"
[#324]: https://github.com/jprichardson/node-fs-extra/pull/324 "copySync() should apply filter to directories like copy() [bug, feature-copy]"
[#323]: https://github.com/jprichardson/node-fs-extra/issues/323 "Support for `dest` being a directory when using `copy*()`?"
[#322]: https://github.com/jprichardson/node-fs-extra/pull/322 "Add fs-promise as fs-extra-promise alternative"
[#321]: https://github.com/jprichardson/node-fs-extra/issues/321 "fs.copy() with clobber set to false return EEXIST error [feature-copy]"
[#320]: https://github.com/jprichardson/node-fs-extra/issues/320 "fs.copySync: Error: EPERM: operation not permitted, unlink "
[#319]: https://github.com/jprichardson/node-fs-extra/issues/319 "Create directory if not exists"
[#318]: https://github.com/jprichardson/node-fs-extra/issues/318 "Support glob patterns [enhancement, future]"
[#317]: https://github.com/jprichardson/node-fs-extra/pull/317 "Adding copy sync test for src file without write perms"
[#316]: https://github.com/jprichardson/node-fs-extra/pull/316 "Remove move()'s broken limit option [feature-move]"
[#315]: https://github.com/jprichardson/node-fs-extra/pull/315 "Fix move clobber tests to work around graceful-fs bug."
[#314]: https://github.com/jprichardson/node-fs-extra/issues/314 "move() limit option [documentation, enhancement, feature-move]"
[#313]: https://github.com/jprichardson/node-fs-extra/pull/313 "Test that remove() ignores glob characters."
[#312]: https://github.com/jprichardson/node-fs-extra/pull/312 "Enhance walkSync() to return items with path and stats [feature-walk]"
[#311]: https://github.com/jprichardson/node-fs-extra/issues/311 "move() not work when dest name not provided [feature-move]"
[#310]: https://github.com/jprichardson/node-fs-extra/issues/310 "Edit walkSync to return items like what walk emits [documentation, enhancement, feature-walk]"
[#309]: https://github.com/jprichardson/node-fs-extra/issues/309 "moveSync support [enhancement, feature-move]"
[#308]: https://github.com/jprichardson/node-fs-extra/pull/308 "Fix incorrect anchor link"
[#307]: https://github.com/jprichardson/node-fs-extra/pull/307 "Fix coverage"
[#306]: https://github.com/jprichardson/node-fs-extra/pull/306 "Update devDeps, fix lint error"
[#305]: https://github.com/jprichardson/node-fs-extra/pull/305 "Re-add Coveralls"
[#304]: https://github.com/jprichardson/node-fs-extra/pull/304 "Remove path-is-absolute [enhancement]"
[#303]: https://github.com/jprichardson/node-fs-extra/pull/303 "Document copySync filter inconsistency [documentation, feature-copy]"
[#302]: https://github.com/jprichardson/node-fs-extra/pull/302 "fix(console): depreciated -> deprecated"
[#301]: https://github.com/jprichardson/node-fs-extra/pull/301 "Remove chmod call from copySync [feature-copy]"
[#300]: https://github.com/jprichardson/node-fs-extra/pull/300 "Inline Rimraf [enhancement, feature-move, feature-remove]"
[#299]: https://github.com/jprichardson/node-fs-extra/pull/299 "Warn when filter is a RegExp [feature-copy]"
[#298]: https://github.com/jprichardson/node-fs-extra/issues/298 "API Docs [documentation]"
[#297]: https://github.com/jprichardson/node-fs-extra/pull/297 "Warn about using preserveTimestamps on 32-bit node"
[#296]: https://github.com/jprichardson/node-fs-extra/pull/296 "Improve EEXIST error message for copySync [enhancement]"
[#295]: https://github.com/jprichardson/node-fs-extra/pull/295 "Depreciate using regular expressions for copy's filter option [documentation]"
[#294]: https://github.com/jprichardson/node-fs-extra/pull/294 "BREAKING: Refactor lib/copy/ncp.js [feature-copy]"
[#293]: https://github.com/jprichardson/node-fs-extra/pull/293 "Update CI configs"
[#292]: https://github.com/jprichardson/node-fs-extra/issues/292 "Rewrite lib/copy/ncp.js [enhancement, feature-copy]"
[#291]: https://github.com/jprichardson/node-fs-extra/pull/291 "Escape '$' in replacement string for async file copying"
[#290]: https://github.com/jprichardson/node-fs-extra/issues/290 "Exclude files pattern while copying using copy.config.js [question]"
[#289]: https://github.com/jprichardson/node-fs-extra/pull/289 "(Closes #271) lib/util/utimes: properly close file descriptors in the event of an error"
[#288]: https://github.com/jprichardson/node-fs-extra/pull/288 "(Closes #271) lib/util/utimes: properly close file descriptors in the event of an error"
[#287]: https://github.com/jprichardson/node-fs-extra/issues/287 "emptyDir() callback arguments are inconsistent [enhancement, feature-remove]"
[#286]: https://github.com/jprichardson/node-fs-extra/pull/286 "Added walkSync function"
[#285]: https://github.com/jprichardson/node-fs-extra/issues/285 "CITGM test failing on s390"
[#284]: https://github.com/jprichardson/node-fs-extra/issues/284 "outputFile method is missing a check to determine if existing item is a folder or not"
[#283]: https://github.com/jprichardson/node-fs-extra/pull/283 "Apply filter also on directories and symlinks for copySync()"
[#282]: https://github.com/jprichardson/node-fs-extra/pull/282 "Apply filter also on directories and symlinks for copySync()"
[#281]: https://github.com/jprichardson/node-fs-extra/issues/281 "remove function executes 'successfully' but doesn't do anything?"
[#280]: https://github.com/jprichardson/node-fs-extra/pull/280 "Disable rimraf globbing"
[#279]: https://github.com/jprichardson/node-fs-extra/issues/279 "Some code is vendored instead of included [awaiting-reply]"
[#278]: https://github.com/jprichardson/node-fs-extra/issues/278 "copy() does not preserve file/directory ownership"
[#277]: https://github.com/jprichardson/node-fs-extra/pull/277 "Mention defaults for clobber and dereference options"
[#276]: https://github.com/jprichardson/node-fs-extra/issues/276 "Cannot connect to Shared Folder [awaiting-reply]"
[#275]: https://github.com/jprichardson/node-fs-extra/issues/275 "EMFILE, too many open files on Mac OS with JSON API"
[#274]: https://github.com/jprichardson/node-fs-extra/issues/274 "Use with memory-fs? [enhancement, future]"
[#273]: https://github.com/jprichardson/node-fs-extra/pull/273 "tests: rename `remote.test.js` to `remove.test.js`"
[#272]: https://github.com/jprichardson/node-fs-extra/issues/272 "Copy clobber flag never err even when true [bug, feature-copy]"
[#271]: https://github.com/jprichardson/node-fs-extra/issues/271 "Unclosed file handle on futimes error"
[#270]: https://github.com/jprichardson/node-fs-extra/issues/270 "copy not working as desired on Windows [feature-copy, platform-windows]"
[#269]: https://github.com/jprichardson/node-fs-extra/issues/269 "Copying with preserveTimeStamps: true is inaccurate using 32bit node [feature-copy]"
[#268]: https://github.com/jprichardson/node-fs-extra/pull/268 "port fix for mkdirp issue #111"
[#267]: https://github.com/jprichardson/node-fs-extra/issues/267 "WARN deprecated wrench@1.5.9: wrench.js is deprecated!"
[#266]: https://github.com/jprichardson/node-fs-extra/issues/266 "fs-extra"
[#265]: https://github.com/jprichardson/node-fs-extra/issues/265 "Link the `fs.stat fs.exists` etc. methods for replace the `fs` module forever?"
[#264]: https://github.com/jprichardson/node-fs-extra/issues/264 "Renaming a file using move fails when a file inside is open (at least on windows) [wont-fix]"
[#263]: https://github.com/jprichardson/node-fs-extra/issues/263 "ENOSYS: function not implemented, link [needs-confirmed]"
[#262]: https://github.com/jprichardson/node-fs-extra/issues/262 "Add .exists() and .existsSync()"
[#261]: https://github.com/jprichardson/node-fs-extra/issues/261 "Cannot read property 'prototype' of undefined"
[#260]: https://github.com/jprichardson/node-fs-extra/pull/260 "use more specific path for method require"
[#259]: https://github.com/jprichardson/node-fs-extra/issues/259 "Feature Request: isEmpty"
[#258]: https://github.com/jprichardson/node-fs-extra/issues/258 "copy files does not preserve file timestamp"
[#257]: https://github.com/jprichardson/node-fs-extra/issues/257 "Copying a file on windows fails"
[#256]: https://github.com/jprichardson/node-fs-extra/pull/256 "Updated Readme "
[#255]: https://github.com/jprichardson/node-fs-extra/issues/255 "Update rimraf required version"
[#254]: https://github.com/jprichardson/node-fs-extra/issues/254 "request for readTree, readTreeSync, walkSync method"
[#253]: https://github.com/jprichardson/node-fs-extra/issues/253 "outputFile does not touch mtime when file exists"
[#252]: https://github.com/jprichardson/node-fs-extra/pull/252 "Fixing problem when copying file with no write permission"
[#251]: https://github.com/jprichardson/node-fs-extra/issues/251 "Just wanted to say thank you"
[#250]: https://github.com/jprichardson/node-fs-extra/issues/250 "`fs.remove()` not removing files (works with `rm -rf`)"
[#249]: https://github.com/jprichardson/node-fs-extra/issues/249 "Just a Question ... Remove Servers"
[#248]: https://github.com/jprichardson/node-fs-extra/issues/248 "Allow option to not preserve permissions for copy"
[#247]: https://github.com/jprichardson/node-fs-extra/issues/247 "Add TypeScript typing directly in the fs-extra package"
[#246]: https://github.com/jprichardson/node-fs-extra/issues/246 "fse.remove() && fse.removeSync() don't throw error on ENOENT file"
[#245]: https://github.com/jprichardson/node-fs-extra/issues/245 "filter for empty dir [enhancement]"
[#244]: https://github.com/jprichardson/node-fs-extra/issues/244 "copySync doesn't apply the filter to directories"
[#243]: https://github.com/jprichardson/node-fs-extra/issues/243 "Can I request fs.walk() to be synchronous?"
[#242]: https://github.com/jprichardson/node-fs-extra/issues/242 "Accidentally truncates file names ending with $$ [bug, feature-copy]"
[#241]: https://github.com/jprichardson/node-fs-extra/pull/241 "Remove link to createOutputStream"
[#240]: https://github.com/jprichardson/node-fs-extra/issues/240 "walkSync request"
[#239]: https://github.com/jprichardson/node-fs-extra/issues/239 "Depreciate regular expressions for copy's filter [documentation, feature-copy]"
[#238]: https://github.com/jprichardson/node-fs-extra/issues/238 "Can't write to files while in a worker thread."
[#237]: https://github.com/jprichardson/node-fs-extra/issues/237 ".ensureDir(..) fails silently when passed an invalid path..."
[#236]: https://github.com/jprichardson/node-fs-extra/issues/236 "[Removed] Filed under wrong repo"
[#235]: https://github.com/jprichardson/node-fs-extra/pull/235 "Adds symlink dereference option to `fse.copySync` (#191)"
[#234]: https://github.com/jprichardson/node-fs-extra/issues/234 "ensureDirSync fails silent when EACCES: permission denied on travis-ci"
[#233]: https://github.com/jprichardson/node-fs-extra/issues/233 "please make sure the first argument in callback is error object [feature-copy]"
[#232]: https://github.com/jprichardson/node-fs-extra/issues/232 "Copy a folder content to its child folder. "
[#231]: https://github.com/jprichardson/node-fs-extra/issues/231 "Adding read/write/output functions for YAML"
[#230]: https://github.com/jprichardson/node-fs-extra/pull/230 "throw error if src and dest are the same to avoid zeroing out + test"
[#229]: https://github.com/jprichardson/node-fs-extra/pull/229 "fix 'TypeError: callback is not a function' in emptyDir"
[#228]: https://github.com/jprichardson/node-fs-extra/pull/228 "Throw error when target is empty so file is not accidentally zeroed out"
[#227]: https://github.com/jprichardson/node-fs-extra/issues/227 "Uncatchable errors when there are invalid arguments [feature-move]"
[#226]: https://github.com/jprichardson/node-fs-extra/issues/226 "Moving to the current directory"
[#225]: https://github.com/jprichardson/node-fs-extra/issues/225 "EBUSY: resource busy or locked, unlink"
[#224]: https://github.com/jprichardson/node-fs-extra/issues/224 "fse.copy ENOENT error"
[#223]: https://github.com/jprichardson/node-fs-extra/issues/223 "Suspicious behavior of fs.existsSync"
[#222]: https://github.com/jprichardson/node-fs-extra/pull/222 "A clearer description of emtpyDir function"
[#221]: https://github.com/jprichardson/node-fs-extra/pull/221 "Update README.md"
[#220]: https://github.com/jprichardson/node-fs-extra/pull/220 "Non-breaking feature: add option 'passStats' to copy methods."
[#219]: https://github.com/jprichardson/node-fs-extra/pull/219 "Add closing parenthesis in copySync example"
[#218]: https://github.com/jprichardson/node-fs-extra/pull/218 "fix #187 #70 options.filter bug"
[#217]: https://github.com/jprichardson/node-fs-extra/pull/217 "fix #187 #70 options.filter bug"
[#216]: https://github.com/jprichardson/node-fs-extra/pull/216 "fix #187 #70 options.filter bug"
[#215]: https://github.com/jprichardson/node-fs-extra/pull/215 "fse.copy throws error when only src and dest provided [bug, documentation, feature-copy]"
[#214]: https://github.com/jprichardson/node-fs-extra/pull/214 "Fixing copySync anchor tag"
[#213]: https://github.com/jprichardson/node-fs-extra/issues/213 "Merge extfs with this repo"
[#212]: https://github.com/jprichardson/node-fs-extra/pull/212 "Update year to 2016 in README.md and LICENSE"
[#211]: https://github.com/jprichardson/node-fs-extra/issues/211 "Not copying all files"
[#210]: https://github.com/jprichardson/node-fs-extra/issues/210 "copy/copySync behave differently when copying a symbolic file [bug, documentation, feature-copy]"
[#209]: https://github.com/jprichardson/node-fs-extra/issues/209 "In Windows invalid directory name causes infinite loop in ensureDir(). [bug]"
[#208]: https://github.com/jprichardson/node-fs-extra/pull/208 "fix options.preserveTimestamps to false in copy-sync by default [feature-copy]"
[#207]: https://github.com/jprichardson/node-fs-extra/issues/207 "Add `compare` suite of functions"
[#206]: https://github.com/jprichardson/node-fs-extra/issues/206 "outputFileSync"
[#205]: https://github.com/jprichardson/node-fs-extra/issues/205 "fix documents about copy/copySync [documentation, feature-copy]"
[#204]: https://github.com/jprichardson/node-fs-extra/pull/204 "allow copy of block and character device files"
[#203]: https://github.com/jprichardson/node-fs-extra/issues/203 "copy method's argument options couldn't be undefined [bug, feature-copy]"
[#202]: https://github.com/jprichardson/node-fs-extra/issues/202 "why there is not a walkSync method?"
[#201]: https://github.com/jprichardson/node-fs-extra/issues/201 "clobber for directories [feature-copy, future]"
[#200]: https://github.com/jprichardson/node-fs-extra/issues/200 "'copySync' doesn't work in sync"
[#199]: https://github.com/jprichardson/node-fs-extra/issues/199 "fs.copySync fails if user does not own file [bug, feature-copy]"
[#198]: https://github.com/jprichardson/node-fs-extra/issues/198 "handle copying between identical files [feature-copy]"
[#197]: https://github.com/jprichardson/node-fs-extra/issues/197 "Missing documentation for `outputFile` `options` 3rd parameter [documentation]"
[#196]: https://github.com/jprichardson/node-fs-extra/issues/196 "copy filter: async function and/or function called with `fs.stat` result [future]"
[#195]: https://github.com/jprichardson/node-fs-extra/issues/195 "How to override with outputFile?"
[#194]: https://github.com/jprichardson/node-fs-extra/pull/194 "allow ensureFile(Sync) to provide data to be written to created file"
[#193]: https://github.com/jprichardson/node-fs-extra/issues/193 "`fs.copy` fails silently if source file is /dev/null [bug, feature-copy]"
[#192]: https://github.com/jprichardson/node-fs-extra/issues/192 "Remove fs.createOutputStream()"
[#191]: https://github.com/jprichardson/node-fs-extra/issues/191 "How to copy symlinks to target as normal folders [feature-copy]"
[#190]: https://github.com/jprichardson/node-fs-extra/pull/190 "copySync to overwrite destination file if readonly and clobber true"
[#189]: https://github.com/jprichardson/node-fs-extra/pull/189 "move.test fix to support CRLF on Windows"
[#188]: https://github.com/jprichardson/node-fs-extra/issues/188 "move.test failing on windows platform"
[#187]: https://github.com/jprichardson/node-fs-extra/issues/187 "Not filter each file, stops on first false [feature-copy]"
[#186]: https://github.com/jprichardson/node-fs-extra/issues/186 "Do you need a .size() function in this module? [future]"
[#185]: https://github.com/jprichardson/node-fs-extra/issues/185 "Doesn't work on NodeJS v4.x"
[#184]: https://github.com/jprichardson/node-fs-extra/issues/184 "CLI equivalent for fs-extra"
[#183]: https://github.com/jprichardson/node-fs-extra/issues/183 "with clobber true, copy and copySync behave differently if destination file is read only [bug, feature-copy]"
[#182]: https://github.com/jprichardson/node-fs-extra/issues/182 "ensureDir(dir, callback) second callback parameter not specified"
[#181]: https://github.com/jprichardson/node-fs-extra/issues/181 "Add ability to remove file securely [enhancement, wont-fix]"
[#180]: https://github.com/jprichardson/node-fs-extra/issues/180 "Filter option doesn't work the same way in copy and copySync [bug, feature-copy]"
[#179]: https://github.com/jprichardson/node-fs-extra/issues/179 "Include opendir"
[#178]: https://github.com/jprichardson/node-fs-extra/issues/178 "ENOTEMPTY is thrown on removeSync "
[#177]: https://github.com/jprichardson/node-fs-extra/issues/177 "fix `remove()` wildcards (introduced by rimraf) [feature-remove]"
[#176]: https://github.com/jprichardson/node-fs-extra/issues/176 "createOutputStream doesn't emit 'end' event"
[#175]: https://github.com/jprichardson/node-fs-extra/issues/175 "[Feature Request].moveSync support [feature-move, future]"
[#174]: https://github.com/jprichardson/node-fs-extra/pull/174 "Fix copy formatting and document options.filter"
[#173]: https://github.com/jprichardson/node-fs-extra/issues/173 "Feature Request: writeJson should mkdirs"
[#172]: https://github.com/jprichardson/node-fs-extra/issues/172 "rename `clobber` flags to `overwrite`"
[#171]: https://github.com/jprichardson/node-fs-extra/issues/171 "remove unnecessary aliases"
[#170]: https://github.com/jprichardson/node-fs-extra/pull/170 "More robust handling of errors moving across virtual drives"
[#169]: https://github.com/jprichardson/node-fs-extra/pull/169 "suppress ensureLink & ensureSymlink dest exists error"
[#168]: https://github.com/jprichardson/node-fs-extra/pull/168 "suppress ensurelink dest exists error"
[#167]: https://github.com/jprichardson/node-fs-extra/pull/167 "Adds basic (string, buffer) support for ensureFile content [future]"
[#166]: https://github.com/jprichardson/node-fs-extra/pull/166 "Adds basic (string, buffer) support for ensureFile content"
[#165]: https://github.com/jprichardson/node-fs-extra/pull/165 "ensure for link & symlink"
[#164]: https://github.com/jprichardson/node-fs-extra/issues/164 "Feature Request: ensureFile to take optional argument for file content"
[#163]: https://github.com/jprichardson/node-fs-extra/issues/163 "ouputJson not formatted out of the box [bug]"
[#162]: https://github.com/jprichardson/node-fs-extra/pull/162 "ensure symlink & link"
[#161]: https://github.com/jprichardson/node-fs-extra/pull/161 "ensure symlink & link"
[#160]: https://github.com/jprichardson/node-fs-extra/pull/160 "ensure symlink & link"
[#159]: https://github.com/jprichardson/node-fs-extra/pull/159 "ensure symlink & link"
[#158]: https://github.com/jprichardson/node-fs-extra/issues/158 "Feature Request: ensureLink and ensureSymlink methods"
[#157]: https://github.com/jprichardson/node-fs-extra/issues/157 "writeJson isn't formatted"
[#156]: https://github.com/jprichardson/node-fs-extra/issues/156 "Promise.promisifyAll doesn't work for some methods"
[#155]: https://github.com/jprichardson/node-fs-extra/issues/155 "Readme"
[#154]: https://github.com/jprichardson/node-fs-extra/issues/154 "/tmp/millis-test-sync"
[#153]: https://github.com/jprichardson/node-fs-extra/pull/153 "Make preserveTimes also work on read-only files. Closes #152"
[#152]: https://github.com/jprichardson/node-fs-extra/issues/152 "fs.copy fails for read-only files with preserveTimestamp=true [feature-copy]"
[#151]: https://github.com/jprichardson/node-fs-extra/issues/151 "TOC does not work correctly on npm [documentation]"
[#150]: https://github.com/jprichardson/node-fs-extra/issues/150 "Remove test file fixtures, create with code."
[#149]: https://github.com/jprichardson/node-fs-extra/issues/149 "/tmp/millis-test-sync"
[#148]: https://github.com/jprichardson/node-fs-extra/issues/148 "split out `Sync` methods in documentation"
[#147]: https://github.com/jprichardson/node-fs-extra/issues/147 "Adding rmdirIfEmpty"
[#146]: https://github.com/jprichardson/node-fs-extra/pull/146 "ensure test.js works"
[#145]: https://github.com/jprichardson/node-fs-extra/issues/145 "Add `fs.exists` and `fs.existsSync` if it doesn't exist."
[#144]: https://github.com/jprichardson/node-fs-extra/issues/144 "tests failing"
[#143]: https://github.com/jprichardson/node-fs-extra/issues/143 "update graceful-fs"
[#142]: https://github.com/jprichardson/node-fs-extra/issues/142 "PrependFile Feature"
[#141]: https://github.com/jprichardson/node-fs-extra/pull/141 "Add option to preserve timestamps"
[#140]: https://github.com/jprichardson/node-fs-extra/issues/140 "Json file reading fails with 'utf8'"
[#139]: https://github.com/jprichardson/node-fs-extra/pull/139 "Preserve file timestamp on copy. Closes #138"
[#138]: https://github.com/jprichardson/node-fs-extra/issues/138 "Preserve timestamps on copying files"
[#137]: https://github.com/jprichardson/node-fs-extra/issues/137 "outputFile/outputJson: Unexpected end of input"
[#136]: https://github.com/jprichardson/node-fs-extra/pull/136 "Update license attribute"
[#135]: https://github.com/jprichardson/node-fs-extra/issues/135 "emptyDir throws Error if no callback is provided"
[#134]: https://github.com/jprichardson/node-fs-extra/pull/134 "Handle EEXIST error when clobbering dir"
[#133]: https://github.com/jprichardson/node-fs-extra/pull/133 "Travis runs with `sudo: false`"
[#132]: https://github.com/jprichardson/node-fs-extra/pull/132 "isDirectory method"
[#131]: https://github.com/jprichardson/node-fs-extra/issues/131 "copySync is not working iojs 1.8.4 on linux [feature-copy]"
[#130]: https://github.com/jprichardson/node-fs-extra/pull/130 "Please review additional features."
[#129]: https://github.com/jprichardson/node-fs-extra/pull/129 "can you review this feature?"
[#128]: https://github.com/jprichardson/node-fs-extra/issues/128 "fsExtra.move(filepath, newPath) broken;"
[#127]: https://github.com/jprichardson/node-fs-extra/issues/127 "consider using fs.access to remove deprecated warnings for fs.exists"
[#126]: https://github.com/jprichardson/node-fs-extra/issues/126 " TypeError: Object #<Object> has no method 'access'"
[#125]: https://github.com/jprichardson/node-fs-extra/issues/125 "Question: What do the *Sync function do different from non-sync"
[#124]: https://github.com/jprichardson/node-fs-extra/issues/124 "move with clobber option 'ENOTEMPTY'"
[#123]: https://github.com/jprichardson/node-fs-extra/issues/123 "Only copy the content of a directory"
[#122]: https://github.com/jprichardson/node-fs-extra/pull/122 "Update section links in README to match current section ids."
[#121]: https://github.com/jprichardson/node-fs-extra/issues/121 "emptyDir is undefined"
[#120]: https://github.com/jprichardson/node-fs-extra/issues/120 "usage bug caused by shallow cloning methods of 'graceful-fs'"
[#119]: https://github.com/jprichardson/node-fs-extra/issues/119 "mkdirs and ensureDir never invoke callback and consume CPU indefinitely if provided a path with invalid characters on Windows"
[#118]: https://github.com/jprichardson/node-fs-extra/pull/118 "createOutputStream"
[#117]: https://github.com/jprichardson/node-fs-extra/pull/117 "Fixed issue with slash separated paths on windows"
[#116]: https://github.com/jprichardson/node-fs-extra/issues/116 "copySync can only copy directories not files [documentation, feature-copy]"
[#115]: https://github.com/jprichardson/node-fs-extra/issues/115 ".Copy & .CopySync [feature-copy]"
[#114]: https://github.com/jprichardson/node-fs-extra/issues/114 "Fails to move (rename) directory to non-empty directory even with clobber: true"
[#113]: https://github.com/jprichardson/node-fs-extra/issues/113 "fs.copy seems to callback early if the destination file already exists"
[#112]: https://github.com/jprichardson/node-fs-extra/pull/112 "Copying a file into an existing directory"
[#111]: https://github.com/jprichardson/node-fs-extra/pull/111 "Moving a file into an existing directory "
[#110]: https://github.com/jprichardson/node-fs-extra/pull/110 "Moving a file into an existing directory"
[#109]: https://github.com/jprichardson/node-fs-extra/issues/109 "fs.move across windows drives fails"
[#108]: https://github.com/jprichardson/node-fs-extra/issues/108 "fse.move directories across multiple devices doesn't work"
[#107]: https://github.com/jprichardson/node-fs-extra/pull/107 "Check if dest path is an existing dir and copy or move source in it"
[#106]: https://github.com/jprichardson/node-fs-extra/issues/106 "fse.copySync crashes while copying across devices D: [feature-copy]"
[#105]: https://github.com/jprichardson/node-fs-extra/issues/105 "fs.copy hangs on iojs"
[#104]: https://github.com/jprichardson/node-fs-extra/issues/104 "fse.move deletes folders [bug]"
[#103]: https://github.com/jprichardson/node-fs-extra/issues/103 "Error: EMFILE with copy"
[#102]: https://github.com/jprichardson/node-fs-extra/issues/102 "touch / touchSync was removed ?"
[#101]: https://github.com/jprichardson/node-fs-extra/issues/101 "fs-extra promisified"
[#100]: https://github.com/jprichardson/node-fs-extra/pull/100 "copy: options object or filter to pass to ncp"
[#99]: https://github.com/jprichardson/node-fs-extra/issues/99 "ensureDir() modes [future]"
[#98]: https://github.com/jprichardson/node-fs-extra/issues/98 "fs.copy() incorrect async behavior [bug]"
[#97]: https://github.com/jprichardson/node-fs-extra/pull/97 "use path.join; fix copySync bug"
[#96]: https://github.com/jprichardson/node-fs-extra/issues/96 "destFolderExists in copySync is always undefined."
[#95]: https://github.com/jprichardson/node-fs-extra/pull/95 "Using graceful-ncp instead of ncp"
[#94]: https://github.com/jprichardson/node-fs-extra/issues/94 "Error: EEXIST, file already exists '../mkdirp/bin/cmd.js' on fs.copySync() [enhancement, feature-copy]"
[#93]: https://github.com/jprichardson/node-fs-extra/issues/93 "Confusing error if drive not mounted [enhancement]"
[#92]: https://github.com/jprichardson/node-fs-extra/issues/92 "Problems with Bluebird"
[#91]: https://github.com/jprichardson/node-fs-extra/issues/91 "fs.copySync('/test', '/haha') is different with 'cp -r /test /haha' [enhancement]"
[#90]: https://github.com/jprichardson/node-fs-extra/issues/90 "Folder creation and file copy is Happening in 64 bit machine but not in 32 bit machine"
[#89]: https://github.com/jprichardson/node-fs-extra/issues/89 "Error: EEXIST using fs-extra's fs.copy to copy a directory on Windows"
[#88]: https://github.com/jprichardson/node-fs-extra/issues/88 "Stacking those libraries"
[#87]: https://github.com/jprichardson/node-fs-extra/issues/87 "createWriteStream + outputFile = ?"
[#86]: https://github.com/jprichardson/node-fs-extra/issues/86 "no moveSync?"
[#85]: https://github.com/jprichardson/node-fs-extra/pull/85 "Copy symlinks in copySync"
[#84]: https://github.com/jprichardson/node-fs-extra/issues/84 "Push latest version to npm ?"
[#83]: https://github.com/jprichardson/node-fs-extra/issues/83 "Prevent copying a directory into itself [feature-copy]"
[#82]: https://github.com/jprichardson/node-fs-extra/pull/82 "README updates for move"
[#81]: https://github.com/jprichardson/node-fs-extra/issues/81 "fd leak after fs.move"
[#80]: https://github.com/jprichardson/node-fs-extra/pull/80 "Preserve file mode in copySync"
[#79]: https://github.com/jprichardson/node-fs-extra/issues/79 "fs.copy only .html file empty"
[#78]: https://github.com/jprichardson/node-fs-extra/pull/78 "copySync was not applying filters to directories"
[#77]: https://github.com/jprichardson/node-fs-extra/issues/77 "Create README reference to bluebird"
[#76]: https://github.com/jprichardson/node-fs-extra/issues/76 "Create README reference to typescript"
[#75]: https://github.com/jprichardson/node-fs-extra/issues/75 "add glob as a dep? [question]"
[#74]: https://github.com/jprichardson/node-fs-extra/pull/74 "including new emptydir module"
[#73]: https://github.com/jprichardson/node-fs-extra/pull/73 "add dependency status in readme"
[#72]: https://github.com/jprichardson/node-fs-extra/pull/72 "Use svg instead of png to get better image quality"
[#71]: https://github.com/jprichardson/node-fs-extra/issues/71 "fse.copy not working on Windows 7 x64 OS, but, copySync does work"
[#70]: https://github.com/jprichardson/node-fs-extra/issues/70 "Not filter each file, stops on first false [bug]"
[#69]: https://github.com/jprichardson/node-fs-extra/issues/69 "How to check if folder exist and read the folder name"
[#68]: https://github.com/jprichardson/node-fs-extra/issues/68 "consider flag to readJsonSync (throw false) [enhancement]"
[#67]: https://github.com/jprichardson/node-fs-extra/issues/67 "docs for readJson incorrectly states that is accepts options"
[#66]: https://github.com/jprichardson/node-fs-extra/issues/66 "ENAMETOOLONG"
[#65]: https://github.com/jprichardson/node-fs-extra/issues/65 "exclude filter in fs.copy"
[#64]: https://github.com/jprichardson/node-fs-extra/issues/64 "Announce: mfs - monitor your fs-extra calls"
[#63]: https://github.com/jprichardson/node-fs-extra/issues/63 "Walk"
[#62]: https://github.com/jprichardson/node-fs-extra/issues/62 "npm install fs-extra doesn't work"
[#61]: https://github.com/jprichardson/node-fs-extra/issues/61 "No longer supports node 0.8 due to use of `^` in package.json dependencies"
[#60]: https://github.com/jprichardson/node-fs-extra/issues/60 "chmod & chown for mkdirs"
[#59]: https://github.com/jprichardson/node-fs-extra/issues/59 "Consider including mkdirp and making fs-extra '--use_strict' safe [question]"
[#58]: https://github.com/jprichardson/node-fs-extra/issues/58 "Stack trace not included in fs.copy error"
[#57]: https://github.com/jprichardson/node-fs-extra/issues/57 "Possible to include wildcards in delete?"
[#56]: https://github.com/jprichardson/node-fs-extra/issues/56 "Crash when have no access to write to destination file in copy "
[#55]: https://github.com/jprichardson/node-fs-extra/issues/55 "Is it possible to have any console output similar to Grunt copy module?"
[#54]: https://github.com/jprichardson/node-fs-extra/issues/54 "`copy` does not preserve file ownership and permissons"
[#53]: https://github.com/jprichardson/node-fs-extra/issues/53 "outputFile() - ability to write data in appending mode"
[#52]: https://github.com/jprichardson/node-fs-extra/pull/52 "This fixes (what I think) is a bug in copySync"
[#51]: https://github.com/jprichardson/node-fs-extra/pull/51 "Add a Bitdeli Badge to README"
[#50]: https://github.com/jprichardson/node-fs-extra/issues/50 "Replace mechanism in createFile"
[#49]: https://github.com/jprichardson/node-fs-extra/pull/49 "update rimraf to v2.2.6"
[#48]: https://github.com/jprichardson/node-fs-extra/issues/48 "fs.copy issue [bug]"
[#47]: https://github.com/jprichardson/node-fs-extra/issues/47 "Bug in copy - callback called on readStream 'close' - Fixed in ncp 0.5.0"
[#46]: https://github.com/jprichardson/node-fs-extra/pull/46 "update copyright year"
[#45]: https://github.com/jprichardson/node-fs-extra/pull/45 "Added note about fse.outputFile() being the one that overwrites"
[#44]: https://github.com/jprichardson/node-fs-extra/pull/44 "Proposal: Stream support"
[#43]: https://github.com/jprichardson/node-fs-extra/issues/43 "Better error reporting "
[#42]: https://github.com/jprichardson/node-fs-extra/issues/42 "Performance issue?"
[#41]: https://github.com/jprichardson/node-fs-extra/pull/41 "There does seem to be a synchronous version now"
[#40]: https://github.com/jprichardson/node-fs-extra/issues/40 "fs.copy throw unexplained error ENOENT, utime "
[#39]: https://github.com/jprichardson/node-fs-extra/pull/39 "Added regression test for copy() return callback on error"
[#38]: https://github.com/jprichardson/node-fs-extra/pull/38 "Return err in copy() fstat cb, because stat could be undefined or null"
[#37]: https://github.com/jprichardson/node-fs-extra/issues/37 "Maybe include a line reader? [enhancement, question]"
[#36]: https://github.com/jprichardson/node-fs-extra/pull/36 "`filter` parameter `fs.copy` and `fs.copySync`"
[#35]: https://github.com/jprichardson/node-fs-extra/pull/35 "`filter` parameter `fs.copy` and `fs.copySync` "
[#34]: https://github.com/jprichardson/node-fs-extra/issues/34 "update docs to include options for JSON methods [enhancement]"
[#33]: https://github.com/jprichardson/node-fs-extra/pull/33 "fs_extra.copySync"
[#32]: https://github.com/jprichardson/node-fs-extra/issues/32 "update to latest jsonfile [enhancement]"
[#31]: https://github.com/jprichardson/node-fs-extra/issues/31 "Add ensure methods [enhancement]"
[#30]: https://github.com/jprichardson/node-fs-extra/issues/30 "update package.json optional dep `graceful-fs`"
[#29]: https://github.com/jprichardson/node-fs-extra/issues/29 "Copy failing if dest directory doesn't exist. Is this intended?"
[#28]: https://github.com/jprichardson/node-fs-extra/issues/28 "homepage field must be a string url. Deleted."
[#27]: https://github.com/jprichardson/node-fs-extra/issues/27 "Update Readme"
[#26]: https://github.com/jprichardson/node-fs-extra/issues/26 "Add readdir recursive method. [enhancement]"
[#25]: https://github.com/jprichardson/node-fs-extra/pull/25 "adding an `.npmignore` file"
[#24]: https://github.com/jprichardson/node-fs-extra/issues/24 "[bug] cannot run in strict mode [bug]"
[#23]: https://github.com/jprichardson/node-fs-extra/issues/23 "`writeJSON()` should create parent directories"
[#22]: https://github.com/jprichardson/node-fs-extra/pull/22 "Add a limit option to mkdirs()"
[#21]: https://github.com/jprichardson/node-fs-extra/issues/21 "touch() in 0.10.0"
[#20]: https://github.com/jprichardson/node-fs-extra/issues/20 "fs.remove yields callback before directory is really deleted"
[#19]: https://github.com/jprichardson/node-fs-extra/issues/19 "fs.copy err is empty array"
[#18]: https://github.com/jprichardson/node-fs-extra/pull/18 "Exposed copyFile Function"
[#17]: https://github.com/jprichardson/node-fs-extra/issues/17 "Use `require('graceful-fs')` if found instead of `require('fs')`"
[#16]: https://github.com/jprichardson/node-fs-extra/pull/16 "Update README.md"
[#15]: https://github.com/jprichardson/node-fs-extra/issues/15 "Implement cp -r but sync aka copySync. [enhancement]"
[#14]: https://github.com/jprichardson/node-fs-extra/issues/14 "fs.mkdirSync is broken in 0.3.1"
[#13]: https://github.com/jprichardson/node-fs-extra/issues/13 "Thoughts on including a directory tree / file watcher? [enhancement, question]"
[#12]: https://github.com/jprichardson/node-fs-extra/issues/12 "copyFile & copyFileSync are global"
[#11]: https://github.com/jprichardson/node-fs-extra/issues/11 "Thoughts on including a file walker? [enhancement, question]"
[#10]: https://github.com/jprichardson/node-fs-extra/issues/10 "move / moveFile API [enhancement]"
[#9]: https://github.com/jprichardson/node-fs-extra/issues/9 "don't import normal fs stuff into fs-extra"
[#8]: https://github.com/jprichardson/node-fs-extra/pull/8 "Update rimraf to latest version"
[#6]: https://github.com/jprichardson/node-fs-extra/issues/6 "Remove CoffeeScript development dependency"
[#5]: https://github.com/jprichardson/node-fs-extra/issues/5 "comments on naming"
[#4]: https://github.com/jprichardson/node-fs-extra/issues/4 "version bump to 0.2"
[#3]: https://github.com/jprichardson/node-fs-extra/pull/3 "Hi! I fixed some code for you!"
[#2]: https://github.com/jprichardson/node-fs-extra/issues/2 "Merge with fs.extra and mkdirp"
[#1]: https://github.com/jprichardson/node-fs-extra/issues/1 "file-extra npm !exist"
+15
View File
@@ -0,0 +1,15 @@
(The MIT License)
Copyright (c) 2011-2017 JP Richardson
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.
+261
View File
@@ -0,0 +1,261 @@
Node.js: fs-extra
=================
`fs-extra` adds file system methods that aren't included in the native `fs` module and adds promise support to the `fs` methods. It also uses [`graceful-fs`](https://github.com/isaacs/node-graceful-fs) to prevent `EMFILE` errors. It should be a drop in replacement for `fs`.
[![npm Package](https://img.shields.io/npm/v/fs-extra.svg)](https://www.npmjs.org/package/fs-extra)
[![License](https://img.shields.io/npm/l/express.svg)](https://github.com/jprichardson/node-fs-extra/blob/master/LICENSE)
[![build status](https://img.shields.io/travis/jprichardson/node-fs-extra/master.svg)](http://travis-ci.org/jprichardson/node-fs-extra)
[![windows Build status](https://img.shields.io/appveyor/ci/jprichardson/node-fs-extra/master.svg?label=windows%20build)](https://ci.appveyor.com/project/jprichardson/node-fs-extra/branch/master)
[![downloads per month](http://img.shields.io/npm/dm/fs-extra.svg)](https://www.npmjs.org/package/fs-extra)
[![Coverage Status](https://img.shields.io/coveralls/github/jprichardson/node-fs-extra/master.svg)](https://coveralls.io/github/jprichardson/node-fs-extra)
[![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com)
Why?
----
I got tired of including `mkdirp`, `rimraf`, and `ncp` in most of my projects.
Installation
------------
npm install fs-extra
Usage
-----
`fs-extra` is a drop in replacement for native `fs`. All methods in `fs` are attached to `fs-extra`. All `fs` methods return promises if the callback isn't passed.
You don't ever need to include the original `fs` module again:
```js
const fs = require('fs') // this is no longer necessary
```
you can now do this:
```js
const fs = require('fs-extra')
```
or if you prefer to make it clear that you're using `fs-extra` and not `fs`, you may want
to name your `fs` variable `fse` like so:
```js
const fse = require('fs-extra')
```
you can also keep both, but it's redundant:
```js
const fs = require('fs')
const fse = require('fs-extra')
```
Sync vs Async vs Async/Await
-------------
Most methods are async by default. All async methods will return a promise if the callback isn't passed.
Sync methods on the other hand will throw if an error occurs.
Also Async/Await will throw an error if one occurs.
Example:
```js
const fs = require('fs-extra')
// Async with promises:
fs.copy('/tmp/myfile', '/tmp/mynewfile')
.then(() => console.log('success!'))
.catch(err => console.error(err))
// Async with callbacks:
fs.copy('/tmp/myfile', '/tmp/mynewfile', err => {
if (err) return console.error(err)
console.log('success!')
})
// Sync:
try {
fs.copySync('/tmp/myfile', '/tmp/mynewfile')
console.log('success!')
} catch (err) {
console.error(err)
}
// Async/Await:
async function copyFiles () {
try {
await fs.copy('/tmp/myfile', '/tmp/mynewfile')
console.log('success!')
} catch (err) {
console.error(err)
}
}
copyFiles()
```
Methods
-------
### Async
- [copy](docs/copy.md)
- [emptyDir](docs/emptyDir.md)
- [ensureFile](docs/ensureFile.md)
- [ensureDir](docs/ensureDir.md)
- [ensureLink](docs/ensureLink.md)
- [ensureSymlink](docs/ensureSymlink.md)
- [mkdirp](docs/ensureDir.md)
- [mkdirs](docs/ensureDir.md)
- [move](docs/move.md)
- [outputFile](docs/outputFile.md)
- [outputJson](docs/outputJson.md)
- [pathExists](docs/pathExists.md)
- [readJson](docs/readJson.md)
- [remove](docs/remove.md)
- [writeJson](docs/writeJson.md)
### Sync
- [copySync](docs/copy-sync.md)
- [emptyDirSync](docs/emptyDir-sync.md)
- [ensureFileSync](docs/ensureFile-sync.md)
- [ensureDirSync](docs/ensureDir-sync.md)
- [ensureLinkSync](docs/ensureLink-sync.md)
- [ensureSymlinkSync](docs/ensureSymlink-sync.md)
- [mkdirpSync](docs/ensureDir-sync.md)
- [mkdirsSync](docs/ensureDir-sync.md)
- [moveSync](docs/move-sync.md)
- [outputFileSync](docs/outputFile-sync.md)
- [outputJsonSync](docs/outputJson-sync.md)
- [pathExistsSync](docs/pathExists-sync.md)
- [readJsonSync](docs/readJson-sync.md)
- [removeSync](docs/remove-sync.md)
- [writeJsonSync](docs/writeJson-sync.md)
**NOTE:** You can still use the native Node.js methods. They are promisified and copied over to `fs-extra`. See [notes on `fs.read()` & `fs.write()`](docs/fs-read-write.md)
### What happened to `walk()` and `walkSync()`?
They were removed from `fs-extra` in v2.0.0. If you need the functionality, `walk` and `walkSync` are available as separate packages, [`klaw`](https://github.com/jprichardson/node-klaw) and [`klaw-sync`](https://github.com/manidlou/node-klaw-sync).
Third Party
-----------
### TypeScript
If you like TypeScript, you can use `fs-extra` with it: https://github.com/DefinitelyTyped/DefinitelyTyped/tree/master/types/fs-extra
### File / Directory Watching
If you want to watch for changes to files or directories, then you should use [chokidar](https://github.com/paulmillr/chokidar).
### Obtain Filesystem (Devices, Partitions) Information
[fs-filesystem](https://github.com/arthurintelligence/node-fs-filesystem) allows you to read the state of the filesystem of the host on which it is run. It returns information about both the devices and the partitions (volumes) of the system.
### Misc.
- [fs-extra-debug](https://github.com/jdxcode/fs-extra-debug) - Send your fs-extra calls to [debug](https://npmjs.org/package/debug).
- [mfs](https://github.com/cadorn/mfs) - Monitor your fs-extra calls.
Hacking on fs-extra
-------------------
Wanna hack on `fs-extra`? Great! Your help is needed! [fs-extra is one of the most depended upon Node.js packages](http://nodei.co/npm/fs-extra.png?downloads=true&downloadRank=true&stars=true). This project
uses [JavaScript Standard Style](https://github.com/feross/standard) - if the name or style choices bother you,
you're gonna have to get over it :) If `standard` is good enough for `npm`, it's good enough for `fs-extra`.
[![js-standard-style](https://cdn.rawgit.com/feross/standard/master/badge.svg)](https://github.com/feross/standard)
What's needed?
- First, take a look at existing issues. Those are probably going to be where the priority lies.
- More tests for edge cases. Specifically on different platforms. There can never be enough tests.
- Improve test coverage. See coveralls output for more info.
Note: If you make any big changes, **you should definitely file an issue for discussion first.**
### Running the Test Suite
fs-extra contains hundreds of tests.
- `npm run lint`: runs the linter ([standard](http://standardjs.com/))
- `npm run unit`: runs the unit tests
- `npm test`: runs both the linter and the tests
### Windows
If you run the tests on the Windows and receive a lot of symbolic link `EPERM` permission errors, it's
because on Windows you need elevated privilege to create symbolic links. You can add this to your Windows's
account by following the instructions here: http://superuser.com/questions/104845/permission-to-make-symbolic-links-in-windows-7
However, I didn't have much luck doing this.
Since I develop on Mac OS X, I use VMWare Fusion for Windows testing. I create a shared folder that I map to a drive on Windows.
I open the `Node.js command prompt` and run as `Administrator`. I then map the network drive running the following command:
net use z: "\\vmware-host\Shared Folders"
I can then navigate to my `fs-extra` directory and run the tests.
Naming
------
I put a lot of thought into the naming of these functions. Inspired by @coolaj86's request. So he deserves much of the credit for raising the issue. See discussion(s) here:
* https://github.com/jprichardson/node-fs-extra/issues/2
* https://github.com/flatiron/utile/issues/11
* https://github.com/ryanmcgrath/wrench-js/issues/29
* https://github.com/substack/node-mkdirp/issues/17
First, I believe that in as many cases as possible, the [Node.js naming schemes](http://nodejs.org/api/fs.html) should be chosen. However, there are problems with the Node.js own naming schemes.
For example, `fs.readFile()` and `fs.readdir()`: the **F** is capitalized in *File* and the **d** is not capitalized in *dir*. Perhaps a bit pedantic, but they should still be consistent. Also, Node.js has chosen a lot of POSIX naming schemes, which I believe is great. See: `fs.mkdir()`, `fs.rmdir()`, `fs.chown()`, etc.
We have a dilemma though. How do you consistently name methods that perform the following POSIX commands: `cp`, `cp -r`, `mkdir -p`, and `rm -rf`?
My perspective: when in doubt, err on the side of simplicity. A directory is just a hierarchical grouping of directories and files. Consider that for a moment. So when you want to copy it or remove it, in most cases you'll want to copy or remove all of its contents. When you want to create a directory, if the directory that it's suppose to be contained in does not exist, then in most cases you'll want to create that too.
So, if you want to remove a file or a directory regardless of whether it has contents, just call `fs.remove(path)`. If you want to copy a file or a directory whether it has contents, just call `fs.copy(source, destination)`. If you want to create a directory regardless of whether its parent directories exist, just call `fs.mkdirs(path)` or `fs.mkdirp(path)`.
Credit
------
`fs-extra` wouldn't be possible without using the modules from the following authors:
- [Isaac Shlueter](https://github.com/isaacs)
- [Charlie McConnel](https://github.com/avianflu)
- [James Halliday](https://github.com/substack)
- [Andrew Kelley](https://github.com/andrewrk)
License
-------
Licensed under MIT
Copyright (c) 2011-2017 [JP Richardson](https://github.com/jprichardson)
[1]: http://nodejs.org/docs/latest/api/fs.html
[jsonfile]: https://github.com/jprichardson/node-jsonfile
@@ -0,0 +1,164 @@
'use strict'
const fs = require('graceful-fs')
const path = require('path')
const mkdirpSync = require('../mkdirs').mkdirsSync
const utimesSync = require('../util/utimes.js').utimesMillisSync
const stat = require('../util/stat')
function copySync (src, dest, opts) {
if (typeof opts === 'function') {
opts = { filter: opts }
}
opts = opts || {}
opts.clobber = 'clobber' in opts ? !!opts.clobber : true // default to true for now
opts.overwrite = 'overwrite' in opts ? !!opts.overwrite : opts.clobber // overwrite falls back to clobber
// Warn about using preserveTimestamps on 32-bit node
if (opts.preserveTimestamps && process.arch === 'ia32') {
console.warn(`fs-extra: Using the preserveTimestamps option in 32-bit node is not recommended;\n
see https://github.com/jprichardson/node-fs-extra/issues/269`)
}
const { srcStat, destStat } = stat.checkPathsSync(src, dest, 'copy')
stat.checkParentPathsSync(src, srcStat, dest, 'copy')
return handleFilterAndCopy(destStat, src, dest, opts)
}
function handleFilterAndCopy (destStat, src, dest, opts) {
if (opts.filter && !opts.filter(src, dest)) return
const destParent = path.dirname(dest)
if (!fs.existsSync(destParent)) mkdirpSync(destParent)
return startCopy(destStat, src, dest, opts)
}
function startCopy (destStat, src, dest, opts) {
if (opts.filter && !opts.filter(src, dest)) return
return getStats(destStat, src, dest, opts)
}
function getStats (destStat, src, dest, opts) {
const statSync = opts.dereference ? fs.statSync : fs.lstatSync
const srcStat = statSync(src)
if (srcStat.isDirectory()) return onDir(srcStat, destStat, src, dest, opts)
else if (srcStat.isFile() ||
srcStat.isCharacterDevice() ||
srcStat.isBlockDevice()) return onFile(srcStat, destStat, src, dest, opts)
else if (srcStat.isSymbolicLink()) return onLink(destStat, src, dest, opts)
}
function onFile (srcStat, destStat, src, dest, opts) {
if (!destStat) return copyFile(srcStat, src, dest, opts)
return mayCopyFile(srcStat, src, dest, opts)
}
function mayCopyFile (srcStat, src, dest, opts) {
if (opts.overwrite) {
fs.unlinkSync(dest)
return copyFile(srcStat, src, dest, opts)
} else if (opts.errorOnExist) {
throw new Error(`'${dest}' already exists`)
}
}
function copyFile (srcStat, src, dest, opts) {
if (typeof fs.copyFileSync === 'function') {
fs.copyFileSync(src, dest)
fs.chmodSync(dest, srcStat.mode)
if (opts.preserveTimestamps) {
return utimesSync(dest, srcStat.atime, srcStat.mtime)
}
return
}
return copyFileFallback(srcStat, src, dest, opts)
}
function copyFileFallback (srcStat, src, dest, opts) {
const BUF_LENGTH = 64 * 1024
const _buff = require('../util/buffer')(BUF_LENGTH)
const fdr = fs.openSync(src, 'r')
const fdw = fs.openSync(dest, 'w', srcStat.mode)
let pos = 0
while (pos < srcStat.size) {
const bytesRead = fs.readSync(fdr, _buff, 0, BUF_LENGTH, pos)
fs.writeSync(fdw, _buff, 0, bytesRead)
pos += bytesRead
}
if (opts.preserveTimestamps) fs.futimesSync(fdw, srcStat.atime, srcStat.mtime)
fs.closeSync(fdr)
fs.closeSync(fdw)
}
function onDir (srcStat, destStat, src, dest, opts) {
if (!destStat) return mkDirAndCopy(srcStat, src, dest, opts)
if (destStat && !destStat.isDirectory()) {
throw new Error(`Cannot overwrite non-directory '${dest}' with directory '${src}'.`)
}
return copyDir(src, dest, opts)
}
function mkDirAndCopy (srcStat, src, dest, opts) {
fs.mkdirSync(dest)
copyDir(src, dest, opts)
return fs.chmodSync(dest, srcStat.mode)
}
function copyDir (src, dest, opts) {
fs.readdirSync(src).forEach(item => copyDirItem(item, src, dest, opts))
}
function copyDirItem (item, src, dest, opts) {
const srcItem = path.join(src, item)
const destItem = path.join(dest, item)
const { destStat } = stat.checkPathsSync(srcItem, destItem, 'copy')
return startCopy(destStat, srcItem, destItem, opts)
}
function onLink (destStat, src, dest, opts) {
let resolvedSrc = fs.readlinkSync(src)
if (opts.dereference) {
resolvedSrc = path.resolve(process.cwd(), resolvedSrc)
}
if (!destStat) {
return fs.symlinkSync(resolvedSrc, dest)
} else {
let resolvedDest
try {
resolvedDest = fs.readlinkSync(dest)
} catch (err) {
// dest exists and is a regular file or directory,
// Windows may throw UNKNOWN error. If dest already exists,
// fs throws error anyway, so no need to guard against it here.
if (err.code === 'EINVAL' || err.code === 'UNKNOWN') return fs.symlinkSync(resolvedSrc, dest)
throw err
}
if (opts.dereference) {
resolvedDest = path.resolve(process.cwd(), resolvedDest)
}
if (stat.isSrcSubdir(resolvedSrc, resolvedDest)) {
throw new Error(`Cannot copy '${resolvedSrc}' to a subdirectory of itself, '${resolvedDest}'.`)
}
// prevent copy if src is a subdir of dest since unlinking
// dest in this case would result in removing src contents
// and therefore a broken symlink would be created.
if (fs.statSync(dest).isDirectory() && stat.isSrcSubdir(resolvedDest, resolvedSrc)) {
throw new Error(`Cannot overwrite '${resolvedDest}' with '${resolvedSrc}'.`)
}
return copyLink(resolvedSrc, dest)
}
}
function copyLink (resolvedSrc, dest) {
fs.unlinkSync(dest)
return fs.symlinkSync(resolvedSrc, dest)
}
module.exports = copySync
@@ -0,0 +1,5 @@
'use strict'
module.exports = {
copySync: require('./copy-sync')
}
+212
View File
@@ -0,0 +1,212 @@
'use strict'
const fs = require('graceful-fs')
const path = require('path')
const mkdirp = require('../mkdirs').mkdirs
const pathExists = require('../path-exists').pathExists
const utimes = require('../util/utimes').utimesMillis
const stat = require('../util/stat')
function copy (src, dest, opts, cb) {
if (typeof opts === 'function' && !cb) {
cb = opts
opts = {}
} else if (typeof opts === 'function') {
opts = { filter: opts }
}
cb = cb || function () {}
opts = opts || {}
opts.clobber = 'clobber' in opts ? !!opts.clobber : true // default to true for now
opts.overwrite = 'overwrite' in opts ? !!opts.overwrite : opts.clobber // overwrite falls back to clobber
// Warn about using preserveTimestamps on 32-bit node
if (opts.preserveTimestamps && process.arch === 'ia32') {
console.warn(`fs-extra: Using the preserveTimestamps option in 32-bit node is not recommended;\n
see https://github.com/jprichardson/node-fs-extra/issues/269`)
}
stat.checkPaths(src, dest, 'copy', (err, stats) => {
if (err) return cb(err)
const { srcStat, destStat } = stats
stat.checkParentPaths(src, srcStat, dest, 'copy', err => {
if (err) return cb(err)
if (opts.filter) return handleFilter(checkParentDir, destStat, src, dest, opts, cb)
return checkParentDir(destStat, src, dest, opts, cb)
})
})
}
function checkParentDir (destStat, src, dest, opts, cb) {
const destParent = path.dirname(dest)
pathExists(destParent, (err, dirExists) => {
if (err) return cb(err)
if (dirExists) return startCopy(destStat, src, dest, opts, cb)
mkdirp(destParent, err => {
if (err) return cb(err)
return startCopy(destStat, src, dest, opts, cb)
})
})
}
function handleFilter (onInclude, destStat, src, dest, opts, cb) {
Promise.resolve(opts.filter(src, dest)).then(include => {
if (include) return onInclude(destStat, src, dest, opts, cb)
return cb()
}, error => cb(error))
}
function startCopy (destStat, src, dest, opts, cb) {
if (opts.filter) return handleFilter(getStats, destStat, src, dest, opts, cb)
return getStats(destStat, src, dest, opts, cb)
}
function getStats (destStat, src, dest, opts, cb) {
const stat = opts.dereference ? fs.stat : fs.lstat
stat(src, (err, srcStat) => {
if (err) return cb(err)
if (srcStat.isDirectory()) return onDir(srcStat, destStat, src, dest, opts, cb)
else if (srcStat.isFile() ||
srcStat.isCharacterDevice() ||
srcStat.isBlockDevice()) return onFile(srcStat, destStat, src, dest, opts, cb)
else if (srcStat.isSymbolicLink()) return onLink(destStat, src, dest, opts, cb)
})
}
function onFile (srcStat, destStat, src, dest, opts, cb) {
if (!destStat) return copyFile(srcStat, src, dest, opts, cb)
return mayCopyFile(srcStat, src, dest, opts, cb)
}
function mayCopyFile (srcStat, src, dest, opts, cb) {
if (opts.overwrite) {
fs.unlink(dest, err => {
if (err) return cb(err)
return copyFile(srcStat, src, dest, opts, cb)
})
} else if (opts.errorOnExist) {
return cb(new Error(`'${dest}' already exists`))
} else return cb()
}
function copyFile (srcStat, src, dest, opts, cb) {
if (typeof fs.copyFile === 'function') {
return fs.copyFile(src, dest, err => {
if (err) return cb(err)
return setDestModeAndTimestamps(srcStat, dest, opts, cb)
})
}
return copyFileFallback(srcStat, src, dest, opts, cb)
}
function copyFileFallback (srcStat, src, dest, opts, cb) {
const rs = fs.createReadStream(src)
rs.on('error', err => cb(err)).once('open', () => {
const ws = fs.createWriteStream(dest, { mode: srcStat.mode })
ws.on('error', err => cb(err))
.on('open', () => rs.pipe(ws))
.once('close', () => setDestModeAndTimestamps(srcStat, dest, opts, cb))
})
}
function setDestModeAndTimestamps (srcStat, dest, opts, cb) {
fs.chmod(dest, srcStat.mode, err => {
if (err) return cb(err)
if (opts.preserveTimestamps) {
return utimes(dest, srcStat.atime, srcStat.mtime, cb)
}
return cb()
})
}
function onDir (srcStat, destStat, src, dest, opts, cb) {
if (!destStat) return mkDirAndCopy(srcStat, src, dest, opts, cb)
if (destStat && !destStat.isDirectory()) {
return cb(new Error(`Cannot overwrite non-directory '${dest}' with directory '${src}'.`))
}
return copyDir(src, dest, opts, cb)
}
function mkDirAndCopy (srcStat, src, dest, opts, cb) {
fs.mkdir(dest, err => {
if (err) return cb(err)
copyDir(src, dest, opts, err => {
if (err) return cb(err)
return fs.chmod(dest, srcStat.mode, cb)
})
})
}
function copyDir (src, dest, opts, cb) {
fs.readdir(src, (err, items) => {
if (err) return cb(err)
return copyDirItems(items, src, dest, opts, cb)
})
}
function copyDirItems (items, src, dest, opts, cb) {
const item = items.pop()
if (!item) return cb()
return copyDirItem(items, item, src, dest, opts, cb)
}
function copyDirItem (items, item, src, dest, opts, cb) {
const srcItem = path.join(src, item)
const destItem = path.join(dest, item)
stat.checkPaths(srcItem, destItem, 'copy', (err, stats) => {
if (err) return cb(err)
const { destStat } = stats
startCopy(destStat, srcItem, destItem, opts, err => {
if (err) return cb(err)
return copyDirItems(items, src, dest, opts, cb)
})
})
}
function onLink (destStat, src, dest, opts, cb) {
fs.readlink(src, (err, resolvedSrc) => {
if (err) return cb(err)
if (opts.dereference) {
resolvedSrc = path.resolve(process.cwd(), resolvedSrc)
}
if (!destStat) {
return fs.symlink(resolvedSrc, dest, cb)
} else {
fs.readlink(dest, (err, resolvedDest) => {
if (err) {
// dest exists and is a regular file or directory,
// Windows may throw UNKNOWN error. If dest already exists,
// fs throws error anyway, so no need to guard against it here.
if (err.code === 'EINVAL' || err.code === 'UNKNOWN') return fs.symlink(resolvedSrc, dest, cb)
return cb(err)
}
if (opts.dereference) {
resolvedDest = path.resolve(process.cwd(), resolvedDest)
}
if (stat.isSrcSubdir(resolvedSrc, resolvedDest)) {
return cb(new Error(`Cannot copy '${resolvedSrc}' to a subdirectory of itself, '${resolvedDest}'.`))
}
// do not copy if src is a subdir of dest since unlinking
// dest in this case would result in removing src contents
// and therefore a broken symlink would be created.
if (destStat.isDirectory() && stat.isSrcSubdir(resolvedDest, resolvedSrc)) {
return cb(new Error(`Cannot overwrite '${resolvedDest}' with '${resolvedSrc}'.`))
}
return copyLink(resolvedSrc, dest, cb)
})
}
})
}
function copyLink (resolvedSrc, dest, cb) {
fs.unlink(dest, err => {
if (err) return cb(err)
return fs.symlink(resolvedSrc, dest, cb)
})
}
module.exports = copy
+6
View File
@@ -0,0 +1,6 @@
'use strict'
const u = require('universalify').fromCallback
module.exports = {
copy: u(require('./copy'))
}
+48
View File
@@ -0,0 +1,48 @@
'use strict'
const u = require('universalify').fromCallback
const fs = require('graceful-fs')
const path = require('path')
const mkdir = require('../mkdirs')
const remove = require('../remove')
const emptyDir = u(function emptyDir (dir, callback) {
callback = callback || function () {}
fs.readdir(dir, (err, items) => {
if (err) return mkdir.mkdirs(dir, callback)
items = items.map(item => path.join(dir, item))
deleteItem()
function deleteItem () {
const item = items.pop()
if (!item) return callback()
remove.remove(item, err => {
if (err) return callback(err)
deleteItem()
})
}
})
})
function emptyDirSync (dir) {
let items
try {
items = fs.readdirSync(dir)
} catch (err) {
return mkdir.mkdirsSync(dir)
}
items.forEach(item => {
item = path.join(dir, item)
remove.removeSync(item)
})
}
module.exports = {
emptyDirSync,
emptydirSync: emptyDirSync,
emptyDir,
emptydir: emptyDir
}
+49
View File
@@ -0,0 +1,49 @@
'use strict'
const u = require('universalify').fromCallback
const path = require('path')
const fs = require('graceful-fs')
const mkdir = require('../mkdirs')
const pathExists = require('../path-exists').pathExists
function createFile (file, callback) {
function makeFile () {
fs.writeFile(file, '', err => {
if (err) return callback(err)
callback()
})
}
fs.stat(file, (err, stats) => { // eslint-disable-line handle-callback-err
if (!err && stats.isFile()) return callback()
const dir = path.dirname(file)
pathExists(dir, (err, dirExists) => {
if (err) return callback(err)
if (dirExists) return makeFile()
mkdir.mkdirs(dir, err => {
if (err) return callback(err)
makeFile()
})
})
})
}
function createFileSync (file) {
let stats
try {
stats = fs.statSync(file)
} catch (e) {}
if (stats && stats.isFile()) return
const dir = path.dirname(file)
if (!fs.existsSync(dir)) {
mkdir.mkdirsSync(dir)
}
fs.writeFileSync(file, '')
}
module.exports = {
createFile: u(createFile),
createFileSync
}
+23
View File
@@ -0,0 +1,23 @@
'use strict'
const file = require('./file')
const link = require('./link')
const symlink = require('./symlink')
module.exports = {
// file
createFile: file.createFile,
createFileSync: file.createFileSync,
ensureFile: file.createFile,
ensureFileSync: file.createFileSync,
// link
createLink: link.createLink,
createLinkSync: link.createLinkSync,
ensureLink: link.createLink,
ensureLinkSync: link.createLinkSync,
// symlink
createSymlink: symlink.createSymlink,
createSymlinkSync: symlink.createSymlinkSync,
ensureSymlink: symlink.createSymlink,
ensureSymlinkSync: symlink.createSymlinkSync
}
+61
View File
@@ -0,0 +1,61 @@
'use strict'
const u = require('universalify').fromCallback
const path = require('path')
const fs = require('graceful-fs')
const mkdir = require('../mkdirs')
const pathExists = require('../path-exists').pathExists
function createLink (srcpath, dstpath, callback) {
function makeLink (srcpath, dstpath) {
fs.link(srcpath, dstpath, err => {
if (err) return callback(err)
callback(null)
})
}
pathExists(dstpath, (err, destinationExists) => {
if (err) return callback(err)
if (destinationExists) return callback(null)
fs.lstat(srcpath, (err) => {
if (err) {
err.message = err.message.replace('lstat', 'ensureLink')
return callback(err)
}
const dir = path.dirname(dstpath)
pathExists(dir, (err, dirExists) => {
if (err) return callback(err)
if (dirExists) return makeLink(srcpath, dstpath)
mkdir.mkdirs(dir, err => {
if (err) return callback(err)
makeLink(srcpath, dstpath)
})
})
})
})
}
function createLinkSync (srcpath, dstpath) {
const destinationExists = fs.existsSync(dstpath)
if (destinationExists) return undefined
try {
fs.lstatSync(srcpath)
} catch (err) {
err.message = err.message.replace('lstat', 'ensureLink')
throw err
}
const dir = path.dirname(dstpath)
const dirExists = fs.existsSync(dir)
if (dirExists) return fs.linkSync(srcpath, dstpath)
mkdir.mkdirsSync(dir)
return fs.linkSync(srcpath, dstpath)
}
module.exports = {
createLink: u(createLink),
createLinkSync
}
@@ -0,0 +1,99 @@
'use strict'
const path = require('path')
const fs = require('graceful-fs')
const pathExists = require('../path-exists').pathExists
/**
* Function that returns two types of paths, one relative to symlink, and one
* relative to the current working directory. Checks if path is absolute or
* relative. If the path is relative, this function checks if the path is
* relative to symlink or relative to current working directory. This is an
* initiative to find a smarter `srcpath` to supply when building symlinks.
* This allows you to determine which path to use out of one of three possible
* types of source paths. The first is an absolute path. This is detected by
* `path.isAbsolute()`. When an absolute path is provided, it is checked to
* see if it exists. If it does it's used, if not an error is returned
* (callback)/ thrown (sync). The other two options for `srcpath` are a
* relative url. By default Node's `fs.symlink` works by creating a symlink
* using `dstpath` and expects the `srcpath` to be relative to the newly
* created symlink. If you provide a `srcpath` that does not exist on the file
* system it results in a broken symlink. To minimize this, the function
* checks to see if the 'relative to symlink' source file exists, and if it
* does it will use it. If it does not, it checks if there's a file that
* exists that is relative to the current working directory, if does its used.
* This preserves the expectations of the original fs.symlink spec and adds
* the ability to pass in `relative to current working direcotry` paths.
*/
function symlinkPaths (srcpath, dstpath, callback) {
if (path.isAbsolute(srcpath)) {
return fs.lstat(srcpath, (err) => {
if (err) {
err.message = err.message.replace('lstat', 'ensureSymlink')
return callback(err)
}
return callback(null, {
'toCwd': srcpath,
'toDst': srcpath
})
})
} else {
const dstdir = path.dirname(dstpath)
const relativeToDst = path.join(dstdir, srcpath)
return pathExists(relativeToDst, (err, exists) => {
if (err) return callback(err)
if (exists) {
return callback(null, {
'toCwd': relativeToDst,
'toDst': srcpath
})
} else {
return fs.lstat(srcpath, (err) => {
if (err) {
err.message = err.message.replace('lstat', 'ensureSymlink')
return callback(err)
}
return callback(null, {
'toCwd': srcpath,
'toDst': path.relative(dstdir, srcpath)
})
})
}
})
}
}
function symlinkPathsSync (srcpath, dstpath) {
let exists
if (path.isAbsolute(srcpath)) {
exists = fs.existsSync(srcpath)
if (!exists) throw new Error('absolute srcpath does not exist')
return {
'toCwd': srcpath,
'toDst': srcpath
}
} else {
const dstdir = path.dirname(dstpath)
const relativeToDst = path.join(dstdir, srcpath)
exists = fs.existsSync(relativeToDst)
if (exists) {
return {
'toCwd': relativeToDst,
'toDst': srcpath
}
} else {
exists = fs.existsSync(srcpath)
if (!exists) throw new Error('relative srcpath does not exist')
return {
'toCwd': srcpath,
'toDst': path.relative(dstdir, srcpath)
}
}
}
}
module.exports = {
symlinkPaths,
symlinkPathsSync
}
@@ -0,0 +1,31 @@
'use strict'
const fs = require('graceful-fs')
function symlinkType (srcpath, type, callback) {
callback = (typeof type === 'function') ? type : callback
type = (typeof type === 'function') ? false : type
if (type) return callback(null, type)
fs.lstat(srcpath, (err, stats) => {
if (err) return callback(null, 'file')
type = (stats && stats.isDirectory()) ? 'dir' : 'file'
callback(null, type)
})
}
function symlinkTypeSync (srcpath, type) {
let stats
if (type) return type
try {
stats = fs.lstatSync(srcpath)
} catch (e) {
return 'file'
}
return (stats && stats.isDirectory()) ? 'dir' : 'file'
}
module.exports = {
symlinkType,
symlinkTypeSync
}
+63
View File
@@ -0,0 +1,63 @@
'use strict'
const u = require('universalify').fromCallback
const path = require('path')
const fs = require('graceful-fs')
const _mkdirs = require('../mkdirs')
const mkdirs = _mkdirs.mkdirs
const mkdirsSync = _mkdirs.mkdirsSync
const _symlinkPaths = require('./symlink-paths')
const symlinkPaths = _symlinkPaths.symlinkPaths
const symlinkPathsSync = _symlinkPaths.symlinkPathsSync
const _symlinkType = require('./symlink-type')
const symlinkType = _symlinkType.symlinkType
const symlinkTypeSync = _symlinkType.symlinkTypeSync
const pathExists = require('../path-exists').pathExists
function createSymlink (srcpath, dstpath, type, callback) {
callback = (typeof type === 'function') ? type : callback
type = (typeof type === 'function') ? false : type
pathExists(dstpath, (err, destinationExists) => {
if (err) return callback(err)
if (destinationExists) return callback(null)
symlinkPaths(srcpath, dstpath, (err, relative) => {
if (err) return callback(err)
srcpath = relative.toDst
symlinkType(relative.toCwd, type, (err, type) => {
if (err) return callback(err)
const dir = path.dirname(dstpath)
pathExists(dir, (err, dirExists) => {
if (err) return callback(err)
if (dirExists) return fs.symlink(srcpath, dstpath, type, callback)
mkdirs(dir, err => {
if (err) return callback(err)
fs.symlink(srcpath, dstpath, type, callback)
})
})
})
})
})
}
function createSymlinkSync (srcpath, dstpath, type) {
const destinationExists = fs.existsSync(dstpath)
if (destinationExists) return undefined
const relative = symlinkPathsSync(srcpath, dstpath)
srcpath = relative.toDst
type = symlinkTypeSync(relative.toCwd, type)
const dir = path.dirname(dstpath)
const exists = fs.existsSync(dir)
if (exists) return fs.symlinkSync(srcpath, dstpath, type)
mkdirsSync(dir)
return fs.symlinkSync(srcpath, dstpath, type)
}
module.exports = {
createSymlink: u(createSymlink),
createSymlinkSync
}
+109
View File
@@ -0,0 +1,109 @@
'use strict'
// This is adapted from https://github.com/normalize/mz
// Copyright (c) 2014-2016 Jonathan Ong me@jongleberry.com and Contributors
const u = require('universalify').fromCallback
const fs = require('graceful-fs')
const api = [
'access',
'appendFile',
'chmod',
'chown',
'close',
'copyFile',
'fchmod',
'fchown',
'fdatasync',
'fstat',
'fsync',
'ftruncate',
'futimes',
'lchown',
'lchmod',
'link',
'lstat',
'mkdir',
'mkdtemp',
'open',
'readFile',
'readdir',
'readlink',
'realpath',
'rename',
'rmdir',
'stat',
'symlink',
'truncate',
'unlink',
'utimes',
'writeFile'
].filter(key => {
// Some commands are not available on some systems. Ex:
// fs.copyFile was added in Node.js v8.5.0
// fs.mkdtemp was added in Node.js v5.10.0
// fs.lchown is not available on at least some Linux
return typeof fs[key] === 'function'
})
// Export all keys:
Object.keys(fs).forEach(key => {
if (key === 'promises') {
// fs.promises is a getter property that triggers ExperimentalWarning
// Don't re-export it here, the getter is defined in "lib/index.js"
return
}
exports[key] = fs[key]
})
// Universalify async methods:
api.forEach(method => {
exports[method] = u(fs[method])
})
// We differ from mz/fs in that we still ship the old, broken, fs.exists()
// since we are a drop-in replacement for the native module
exports.exists = function (filename, callback) {
if (typeof callback === 'function') {
return fs.exists(filename, callback)
}
return new Promise(resolve => {
return fs.exists(filename, resolve)
})
}
// fs.read() & fs.write need special treatment due to multiple callback args
exports.read = function (fd, buffer, offset, length, position, callback) {
if (typeof callback === 'function') {
return fs.read(fd, buffer, offset, length, position, callback)
}
return new Promise((resolve, reject) => {
fs.read(fd, buffer, offset, length, position, (err, bytesRead, buffer) => {
if (err) return reject(err)
resolve({ bytesRead, buffer })
})
})
}
// Function signature can be
// fs.write(fd, buffer[, offset[, length[, position]]], callback)
// OR
// fs.write(fd, string[, position[, encoding]], callback)
// We need to handle both cases, so we use ...args
exports.write = function (fd, buffer, ...args) {
if (typeof args[args.length - 1] === 'function') {
return fs.write(fd, buffer, ...args)
}
return new Promise((resolve, reject) => {
fs.write(fd, buffer, ...args, (err, bytesWritten, buffer) => {
if (err) return reject(err)
resolve({ bytesWritten, buffer })
})
})
}
// fs.realpath.native only available in Node v9.2+
if (typeof fs.realpath.native === 'function') {
exports.realpath.native = u(fs.realpath.native)
}
+28
View File
@@ -0,0 +1,28 @@
'use strict'
module.exports = Object.assign(
{},
// Export promiseified graceful-fs:
require('./fs'),
// Export extra methods:
require('./copy-sync'),
require('./copy'),
require('./empty'),
require('./ensure'),
require('./json'),
require('./mkdirs'),
require('./move-sync'),
require('./move'),
require('./output'),
require('./path-exists'),
require('./remove')
)
// Export fs.promises as a getter property so that we don't trigger
// ExperimentalWarning before fs.promises is actually accessed.
const fs = require('fs')
if (Object.getOwnPropertyDescriptor(fs, 'promises')) {
Object.defineProperty(module.exports, 'promises', {
get () { return fs.promises }
})
}
+16
View File
@@ -0,0 +1,16 @@
'use strict'
const u = require('universalify').fromCallback
const jsonFile = require('./jsonfile')
jsonFile.outputJson = u(require('./output-json'))
jsonFile.outputJsonSync = require('./output-json-sync')
// aliases
jsonFile.outputJSON = jsonFile.outputJson
jsonFile.outputJSONSync = jsonFile.outputJsonSync
jsonFile.writeJSON = jsonFile.writeJson
jsonFile.writeJSONSync = jsonFile.writeJsonSync
jsonFile.readJSON = jsonFile.readJson
jsonFile.readJSONSync = jsonFile.readJsonSync
module.exports = jsonFile
+12
View File
@@ -0,0 +1,12 @@
'use strict'
const u = require('universalify').fromCallback
const jsonFile = require('jsonfile')
module.exports = {
// jsonfile exports
readJson: u(jsonFile.readFile),
readJsonSync: jsonFile.readFileSync,
writeJson: u(jsonFile.writeFile),
writeJsonSync: jsonFile.writeFileSync
}
@@ -0,0 +1,18 @@
'use strict'
const fs = require('graceful-fs')
const path = require('path')
const mkdir = require('../mkdirs')
const jsonFile = require('./jsonfile')
function outputJsonSync (file, data, options) {
const dir = path.dirname(file)
if (!fs.existsSync(dir)) {
mkdir.mkdirsSync(dir)
}
jsonFile.writeJsonSync(file, data, options)
}
module.exports = outputJsonSync
@@ -0,0 +1,27 @@
'use strict'
const path = require('path')
const mkdir = require('../mkdirs')
const pathExists = require('../path-exists').pathExists
const jsonFile = require('./jsonfile')
function outputJson (file, data, options, callback) {
if (typeof options === 'function') {
callback = options
options = {}
}
const dir = path.dirname(file)
pathExists(dir, (err, itDoes) => {
if (err) return callback(err)
if (itDoes) return jsonFile.writeJson(file, data, options, callback)
mkdir.mkdirs(dir, err => {
if (err) return callback(err)
jsonFile.writeJson(file, data, options, callback)
})
})
}
module.exports = outputJson
+14
View File
@@ -0,0 +1,14 @@
'use strict'
const u = require('universalify').fromCallback
const mkdirs = u(require('./mkdirs'))
const mkdirsSync = require('./mkdirs-sync')
module.exports = {
mkdirs,
mkdirsSync,
// alias
mkdirp: mkdirs,
mkdirpSync: mkdirsSync,
ensureDir: mkdirs,
ensureDirSync: mkdirsSync
}
@@ -0,0 +1,54 @@
'use strict'
const fs = require('graceful-fs')
const path = require('path')
const invalidWin32Path = require('./win32').invalidWin32Path
const o777 = parseInt('0777', 8)
function mkdirsSync (p, opts, made) {
if (!opts || typeof opts !== 'object') {
opts = { mode: opts }
}
let mode = opts.mode
const xfs = opts.fs || fs
if (process.platform === 'win32' && invalidWin32Path(p)) {
const errInval = new Error(p + ' contains invalid WIN32 path characters.')
errInval.code = 'EINVAL'
throw errInval
}
if (mode === undefined) {
mode = o777 & (~process.umask())
}
if (!made) made = null
p = path.resolve(p)
try {
xfs.mkdirSync(p, mode)
made = made || p
} catch (err0) {
if (err0.code === 'ENOENT') {
if (path.dirname(p) === p) throw err0
made = mkdirsSync(path.dirname(p), opts, made)
mkdirsSync(p, opts, made)
} else {
// In the case of any other error, just see if there's a dir there
// already. If so, then hooray! If not, then something is borked.
let stat
try {
stat = xfs.statSync(p)
} catch (err1) {
throw err0
}
if (!stat.isDirectory()) throw err0
}
}
return made
}
module.exports = mkdirsSync
+63
View File
@@ -0,0 +1,63 @@
'use strict'
const fs = require('graceful-fs')
const path = require('path')
const invalidWin32Path = require('./win32').invalidWin32Path
const o777 = parseInt('0777', 8)
function mkdirs (p, opts, callback, made) {
if (typeof opts === 'function') {
callback = opts
opts = {}
} else if (!opts || typeof opts !== 'object') {
opts = { mode: opts }
}
if (process.platform === 'win32' && invalidWin32Path(p)) {
const errInval = new Error(p + ' contains invalid WIN32 path characters.')
errInval.code = 'EINVAL'
return callback(errInval)
}
let mode = opts.mode
const xfs = opts.fs || fs
if (mode === undefined) {
mode = o777 & (~process.umask())
}
if (!made) made = null
callback = callback || function () {}
p = path.resolve(p)
xfs.mkdir(p, mode, er => {
if (!er) {
made = made || p
return callback(null, made)
}
switch (er.code) {
case 'ENOENT':
if (path.dirname(p) === p) return callback(er)
mkdirs(path.dirname(p), opts, (er, made) => {
if (er) callback(er, made)
else mkdirs(p, opts, callback, made)
})
break
// In the case of any other error, just see if there's a dir
// there already. If so, then hooray! If not, then something
// is borked.
default:
xfs.stat(p, (er2, stat) => {
// if the stat fails, then that's super weird.
// let the original error be the failure reason.
if (er2 || !stat.isDirectory()) callback(er, made)
else callback(null, made)
})
break
}
})
}
module.exports = mkdirs
+25
View File
@@ -0,0 +1,25 @@
'use strict'
const path = require('path')
// get drive on windows
function getRootPath (p) {
p = path.normalize(path.resolve(p)).split(path.sep)
if (p.length > 0) return p[0]
return null
}
// http://stackoverflow.com/a/62888/10333 contains more accurate
// TODO: expand to include the rest
const INVALID_PATH_CHARS = /[<>:"|?*]/
function invalidWin32Path (p) {
const rp = getRootPath(p)
p = p.replace(rp, '')
return INVALID_PATH_CHARS.test(p)
}
module.exports = {
getRootPath,
invalidWin32Path
}
@@ -0,0 +1,5 @@
'use strict'
module.exports = {
moveSync: require('./move-sync')
}
@@ -0,0 +1,47 @@
'use strict'
const fs = require('graceful-fs')
const path = require('path')
const copySync = require('../copy-sync').copySync
const removeSync = require('../remove').removeSync
const mkdirpSync = require('../mkdirs').mkdirpSync
const stat = require('../util/stat')
function moveSync (src, dest, opts) {
opts = opts || {}
const overwrite = opts.overwrite || opts.clobber || false
const { srcStat } = stat.checkPathsSync(src, dest, 'move')
stat.checkParentPathsSync(src, srcStat, dest, 'move')
mkdirpSync(path.dirname(dest))
return doRename(src, dest, overwrite)
}
function doRename (src, dest, overwrite) {
if (overwrite) {
removeSync(dest)
return rename(src, dest, overwrite)
}
if (fs.existsSync(dest)) throw new Error('dest already exists.')
return rename(src, dest, overwrite)
}
function rename (src, dest, overwrite) {
try {
fs.renameSync(src, dest)
} catch (err) {
if (err.code !== 'EXDEV') throw err
return moveAcrossDevice(src, dest, overwrite)
}
}
function moveAcrossDevice (src, dest, overwrite) {
const opts = {
overwrite,
errorOnExist: true
}
copySync(src, dest, opts)
return removeSync(src)
}
module.exports = moveSync
+6
View File
@@ -0,0 +1,6 @@
'use strict'
const u = require('universalify').fromCallback
module.exports = {
move: u(require('./move'))
}
+65
View File
@@ -0,0 +1,65 @@
'use strict'
const fs = require('graceful-fs')
const path = require('path')
const copy = require('../copy').copy
const remove = require('../remove').remove
const mkdirp = require('../mkdirs').mkdirp
const pathExists = require('../path-exists').pathExists
const stat = require('../util/stat')
function move (src, dest, opts, cb) {
if (typeof opts === 'function') {
cb = opts
opts = {}
}
const overwrite = opts.overwrite || opts.clobber || false
stat.checkPaths(src, dest, 'move', (err, stats) => {
if (err) return cb(err)
const { srcStat } = stats
stat.checkParentPaths(src, srcStat, dest, 'move', err => {
if (err) return cb(err)
mkdirp(path.dirname(dest), err => {
if (err) return cb(err)
return doRename(src, dest, overwrite, cb)
})
})
})
}
function doRename (src, dest, overwrite, cb) {
if (overwrite) {
return remove(dest, err => {
if (err) return cb(err)
return rename(src, dest, overwrite, cb)
})
}
pathExists(dest, (err, destExists) => {
if (err) return cb(err)
if (destExists) return cb(new Error('dest already exists.'))
return rename(src, dest, overwrite, cb)
})
}
function rename (src, dest, overwrite, cb) {
fs.rename(src, dest, err => {
if (!err) return cb()
if (err.code !== 'EXDEV') return cb(err)
return moveAcrossDevice(src, dest, overwrite, cb)
})
}
function moveAcrossDevice (src, dest, overwrite, cb) {
const opts = {
overwrite,
errorOnExist: true
}
copy(src, dest, opts, err => {
if (err) return cb(err)
return remove(src, cb)
})
}
module.exports = move
+40
View File
@@ -0,0 +1,40 @@
'use strict'
const u = require('universalify').fromCallback
const fs = require('graceful-fs')
const path = require('path')
const mkdir = require('../mkdirs')
const pathExists = require('../path-exists').pathExists
function outputFile (file, data, encoding, callback) {
if (typeof encoding === 'function') {
callback = encoding
encoding = 'utf8'
}
const dir = path.dirname(file)
pathExists(dir, (err, itDoes) => {
if (err) return callback(err)
if (itDoes) return fs.writeFile(file, data, encoding, callback)
mkdir.mkdirs(dir, err => {
if (err) return callback(err)
fs.writeFile(file, data, encoding, callback)
})
})
}
function outputFileSync (file, ...args) {
const dir = path.dirname(file)
if (fs.existsSync(dir)) {
return fs.writeFileSync(file, ...args)
}
mkdir.mkdirsSync(dir)
fs.writeFileSync(file, ...args)
}
module.exports = {
outputFile: u(outputFile),
outputFileSync
}
@@ -0,0 +1,12 @@
'use strict'
const u = require('universalify').fromPromise
const fs = require('../fs')
function pathExists (path) {
return fs.access(path).then(() => true).catch(() => false)
}
module.exports = {
pathExists: u(pathExists),
pathExistsSync: fs.existsSync
}
+9
View File
@@ -0,0 +1,9 @@
'use strict'
const u = require('universalify').fromCallback
const rimraf = require('./rimraf')
module.exports = {
remove: u(rimraf),
removeSync: rimraf.sync
}
+314
View File
@@ -0,0 +1,314 @@
'use strict'
const fs = require('graceful-fs')
const path = require('path')
const assert = require('assert')
const isWindows = (process.platform === 'win32')
function defaults (options) {
const methods = [
'unlink',
'chmod',
'stat',
'lstat',
'rmdir',
'readdir'
]
methods.forEach(m => {
options[m] = options[m] || fs[m]
m = m + 'Sync'
options[m] = options[m] || fs[m]
})
options.maxBusyTries = options.maxBusyTries || 3
}
function rimraf (p, options, cb) {
let busyTries = 0
if (typeof options === 'function') {
cb = options
options = {}
}
assert(p, 'rimraf: missing path')
assert.strictEqual(typeof p, 'string', 'rimraf: path should be a string')
assert.strictEqual(typeof cb, 'function', 'rimraf: callback function required')
assert(options, 'rimraf: invalid options argument provided')
assert.strictEqual(typeof options, 'object', 'rimraf: options should be object')
defaults(options)
rimraf_(p, options, function CB (er) {
if (er) {
if ((er.code === 'EBUSY' || er.code === 'ENOTEMPTY' || er.code === 'EPERM') &&
busyTries < options.maxBusyTries) {
busyTries++
const time = busyTries * 100
// try again, with the same exact callback as this one.
return setTimeout(() => rimraf_(p, options, CB), time)
}
// already gone
if (er.code === 'ENOENT') er = null
}
cb(er)
})
}
// Two possible strategies.
// 1. Assume it's a file. unlink it, then do the dir stuff on EPERM or EISDIR
// 2. Assume it's a directory. readdir, then do the file stuff on ENOTDIR
//
// Both result in an extra syscall when you guess wrong. However, there
// are likely far more normal files in the world than directories. This
// is based on the assumption that a the average number of files per
// directory is >= 1.
//
// If anyone ever complains about this, then I guess the strategy could
// be made configurable somehow. But until then, YAGNI.
function rimraf_ (p, options, cb) {
assert(p)
assert(options)
assert(typeof cb === 'function')
// sunos lets the root user unlink directories, which is... weird.
// so we have to lstat here and make sure it's not a dir.
options.lstat(p, (er, st) => {
if (er && er.code === 'ENOENT') {
return cb(null)
}
// Windows can EPERM on stat. Life is suffering.
if (er && er.code === 'EPERM' && isWindows) {
return fixWinEPERM(p, options, er, cb)
}
if (st && st.isDirectory()) {
return rmdir(p, options, er, cb)
}
options.unlink(p, er => {
if (er) {
if (er.code === 'ENOENT') {
return cb(null)
}
if (er.code === 'EPERM') {
return (isWindows)
? fixWinEPERM(p, options, er, cb)
: rmdir(p, options, er, cb)
}
if (er.code === 'EISDIR') {
return rmdir(p, options, er, cb)
}
}
return cb(er)
})
})
}
function fixWinEPERM (p, options, er, cb) {
assert(p)
assert(options)
assert(typeof cb === 'function')
if (er) {
assert(er instanceof Error)
}
options.chmod(p, 0o666, er2 => {
if (er2) {
cb(er2.code === 'ENOENT' ? null : er)
} else {
options.stat(p, (er3, stats) => {
if (er3) {
cb(er3.code === 'ENOENT' ? null : er)
} else if (stats.isDirectory()) {
rmdir(p, options, er, cb)
} else {
options.unlink(p, cb)
}
})
}
})
}
function fixWinEPERMSync (p, options, er) {
let stats
assert(p)
assert(options)
if (er) {
assert(er instanceof Error)
}
try {
options.chmodSync(p, 0o666)
} catch (er2) {
if (er2.code === 'ENOENT') {
return
} else {
throw er
}
}
try {
stats = options.statSync(p)
} catch (er3) {
if (er3.code === 'ENOENT') {
return
} else {
throw er
}
}
if (stats.isDirectory()) {
rmdirSync(p, options, er)
} else {
options.unlinkSync(p)
}
}
function rmdir (p, options, originalEr, cb) {
assert(p)
assert(options)
if (originalEr) {
assert(originalEr instanceof Error)
}
assert(typeof cb === 'function')
// try to rmdir first, and only readdir on ENOTEMPTY or EEXIST (SunOS)
// if we guessed wrong, and it's not a directory, then
// raise the original error.
options.rmdir(p, er => {
if (er && (er.code === 'ENOTEMPTY' || er.code === 'EEXIST' || er.code === 'EPERM')) {
rmkids(p, options, cb)
} else if (er && er.code === 'ENOTDIR') {
cb(originalEr)
} else {
cb(er)
}
})
}
function rmkids (p, options, cb) {
assert(p)
assert(options)
assert(typeof cb === 'function')
options.readdir(p, (er, files) => {
if (er) return cb(er)
let n = files.length
let errState
if (n === 0) return options.rmdir(p, cb)
files.forEach(f => {
rimraf(path.join(p, f), options, er => {
if (errState) {
return
}
if (er) return cb(errState = er)
if (--n === 0) {
options.rmdir(p, cb)
}
})
})
})
}
// this looks simpler, and is strictly *faster*, but will
// tie up the JavaScript thread and fail on excessively
// deep directory trees.
function rimrafSync (p, options) {
let st
options = options || {}
defaults(options)
assert(p, 'rimraf: missing path')
assert.strictEqual(typeof p, 'string', 'rimraf: path should be a string')
assert(options, 'rimraf: missing options')
assert.strictEqual(typeof options, 'object', 'rimraf: options should be object')
try {
st = options.lstatSync(p)
} catch (er) {
if (er.code === 'ENOENT') {
return
}
// Windows can EPERM on stat. Life is suffering.
if (er.code === 'EPERM' && isWindows) {
fixWinEPERMSync(p, options, er)
}
}
try {
// sunos lets the root user unlink directories, which is... weird.
if (st && st.isDirectory()) {
rmdirSync(p, options, null)
} else {
options.unlinkSync(p)
}
} catch (er) {
if (er.code === 'ENOENT') {
return
} else if (er.code === 'EPERM') {
return isWindows ? fixWinEPERMSync(p, options, er) : rmdirSync(p, options, er)
} else if (er.code !== 'EISDIR') {
throw er
}
rmdirSync(p, options, er)
}
}
function rmdirSync (p, options, originalEr) {
assert(p)
assert(options)
if (originalEr) {
assert(originalEr instanceof Error)
}
try {
options.rmdirSync(p)
} catch (er) {
if (er.code === 'ENOTDIR') {
throw originalEr
} else if (er.code === 'ENOTEMPTY' || er.code === 'EEXIST' || er.code === 'EPERM') {
rmkidsSync(p, options)
} else if (er.code !== 'ENOENT') {
throw er
}
}
}
function rmkidsSync (p, options) {
assert(p)
assert(options)
options.readdirSync(p).forEach(f => rimrafSync(path.join(p, f), options))
if (isWindows) {
// We only end up here once we got ENOTEMPTY at least once, and
// at this point, we are guaranteed to have removed all the kids.
// So, we know that it won't be ENOENT or ENOTDIR or anything else.
// try really hard to delete stuff on windows, because it has a
// PROFOUNDLY annoying habit of not closing handles promptly when
// files are deleted, resulting in spurious ENOTEMPTY errors.
const startTime = Date.now()
do {
try {
const ret = options.rmdirSync(p, options)
return ret
} catch (er) { }
} while (Date.now() - startTime < 500) // give up after 500ms
} else {
const ret = options.rmdirSync(p, options)
return ret
}
}
module.exports = rimraf
rimraf.sync = rimrafSync
+12
View File
@@ -0,0 +1,12 @@
'use strict'
/* eslint-disable node/no-deprecated-api */
module.exports = function (size) {
if (typeof Buffer.allocUnsafe === 'function') {
try {
return Buffer.allocUnsafe(size)
} catch (e) {
return new Buffer(size)
}
}
return new Buffer(size)
}
+172
View File
@@ -0,0 +1,172 @@
'use strict'
const fs = require('graceful-fs')
const path = require('path')
const NODE_VERSION_MAJOR_WITH_BIGINT = 10
const NODE_VERSION_MINOR_WITH_BIGINT = 5
const NODE_VERSION_PATCH_WITH_BIGINT = 0
const nodeVersion = process.versions.node.split('.')
const nodeVersionMajor = Number.parseInt(nodeVersion[0], 10)
const nodeVersionMinor = Number.parseInt(nodeVersion[1], 10)
const nodeVersionPatch = Number.parseInt(nodeVersion[2], 10)
function nodeSupportsBigInt () {
if (nodeVersionMajor > NODE_VERSION_MAJOR_WITH_BIGINT) {
return true
} else if (nodeVersionMajor === NODE_VERSION_MAJOR_WITH_BIGINT) {
if (nodeVersionMinor > NODE_VERSION_MINOR_WITH_BIGINT) {
return true
} else if (nodeVersionMinor === NODE_VERSION_MINOR_WITH_BIGINT) {
if (nodeVersionPatch >= NODE_VERSION_PATCH_WITH_BIGINT) {
return true
}
}
}
return false
}
function getStats (src, dest, cb) {
if (nodeSupportsBigInt()) {
fs.stat(src, { bigint: true }, (err, srcStat) => {
if (err) return cb(err)
fs.stat(dest, { bigint: true }, (err, destStat) => {
if (err) {
if (err.code === 'ENOENT') return cb(null, { srcStat, destStat: null })
return cb(err)
}
return cb(null, { srcStat, destStat })
})
})
} else {
fs.stat(src, (err, srcStat) => {
if (err) return cb(err)
fs.stat(dest, (err, destStat) => {
if (err) {
if (err.code === 'ENOENT') return cb(null, { srcStat, destStat: null })
return cb(err)
}
return cb(null, { srcStat, destStat })
})
})
}
}
function getStatsSync (src, dest) {
let srcStat, destStat
if (nodeSupportsBigInt()) {
srcStat = fs.statSync(src, { bigint: true })
} else {
srcStat = fs.statSync(src)
}
try {
if (nodeSupportsBigInt()) {
destStat = fs.statSync(dest, { bigint: true })
} else {
destStat = fs.statSync(dest)
}
} catch (err) {
if (err.code === 'ENOENT') return { srcStat, destStat: null }
throw err
}
return { srcStat, destStat }
}
function checkPaths (src, dest, funcName, cb) {
getStats(src, dest, (err, stats) => {
if (err) return cb(err)
const { srcStat, destStat } = stats
if (destStat && destStat.ino && destStat.dev && destStat.ino === srcStat.ino && destStat.dev === srcStat.dev) {
return cb(new Error('Source and destination must not be the same.'))
}
if (srcStat.isDirectory() && isSrcSubdir(src, dest)) {
return cb(new Error(errMsg(src, dest, funcName)))
}
return cb(null, { srcStat, destStat })
})
}
function checkPathsSync (src, dest, funcName) {
const { srcStat, destStat } = getStatsSync(src, dest)
if (destStat && destStat.ino && destStat.dev && destStat.ino === srcStat.ino && destStat.dev === srcStat.dev) {
throw new Error('Source and destination must not be the same.')
}
if (srcStat.isDirectory() && isSrcSubdir(src, dest)) {
throw new Error(errMsg(src, dest, funcName))
}
return { srcStat, destStat }
}
// recursively check if dest parent is a subdirectory of src.
// It works for all file types including symlinks since it
// checks the src and dest inodes. It starts from the deepest
// parent and stops once it reaches the src parent or the root path.
function checkParentPaths (src, srcStat, dest, funcName, cb) {
const srcParent = path.resolve(path.dirname(src))
const destParent = path.resolve(path.dirname(dest))
if (destParent === srcParent || destParent === path.parse(destParent).root) return cb()
if (nodeSupportsBigInt()) {
fs.stat(destParent, { bigint: true }, (err, destStat) => {
if (err) {
if (err.code === 'ENOENT') return cb()
return cb(err)
}
if (destStat.ino && destStat.dev && destStat.ino === srcStat.ino && destStat.dev === srcStat.dev) {
return cb(new Error(errMsg(src, dest, funcName)))
}
return checkParentPaths(src, srcStat, destParent, funcName, cb)
})
} else {
fs.stat(destParent, (err, destStat) => {
if (err) {
if (err.code === 'ENOENT') return cb()
return cb(err)
}
if (destStat.ino && destStat.dev && destStat.ino === srcStat.ino && destStat.dev === srcStat.dev) {
return cb(new Error(errMsg(src, dest, funcName)))
}
return checkParentPaths(src, srcStat, destParent, funcName, cb)
})
}
}
function checkParentPathsSync (src, srcStat, dest, funcName) {
const srcParent = path.resolve(path.dirname(src))
const destParent = path.resolve(path.dirname(dest))
if (destParent === srcParent || destParent === path.parse(destParent).root) return
let destStat
try {
if (nodeSupportsBigInt()) {
destStat = fs.statSync(destParent, { bigint: true })
} else {
destStat = fs.statSync(destParent)
}
} catch (err) {
if (err.code === 'ENOENT') return
throw err
}
if (destStat.ino && destStat.dev && destStat.ino === srcStat.ino && destStat.dev === srcStat.dev) {
throw new Error(errMsg(src, dest, funcName))
}
return checkParentPathsSync(src, srcStat, destParent, funcName)
}
// return true if dest is a subdir of src, otherwise false.
// It only checks the path strings.
function isSrcSubdir (src, dest) {
const srcArr = path.resolve(src).split(path.sep).filter(i => i)
const destArr = path.resolve(dest).split(path.sep).filter(i => i)
return srcArr.reduce((acc, cur, i) => acc && destArr[i] === cur, true)
}
function errMsg (src, dest, funcName) {
return `Cannot ${funcName} '${src}' to a subdirectory of itself, '${dest}'.`
}
module.exports = {
checkPaths,
checkPathsSync,
checkParentPaths,
checkParentPathsSync,
isSrcSubdir
}
+79
View File
@@ -0,0 +1,79 @@
'use strict'
const fs = require('graceful-fs')
const os = require('os')
const path = require('path')
// HFS, ext{2,3}, FAT do not, Node.js v0.10 does not
function hasMillisResSync () {
let tmpfile = path.join('millis-test-sync' + Date.now().toString() + Math.random().toString().slice(2))
tmpfile = path.join(os.tmpdir(), tmpfile)
// 550 millis past UNIX epoch
const d = new Date(1435410243862)
fs.writeFileSync(tmpfile, 'https://github.com/jprichardson/node-fs-extra/pull/141')
const fd = fs.openSync(tmpfile, 'r+')
fs.futimesSync(fd, d, d)
fs.closeSync(fd)
return fs.statSync(tmpfile).mtime > 1435410243000
}
function hasMillisRes (callback) {
let tmpfile = path.join('millis-test' + Date.now().toString() + Math.random().toString().slice(2))
tmpfile = path.join(os.tmpdir(), tmpfile)
// 550 millis past UNIX epoch
const d = new Date(1435410243862)
fs.writeFile(tmpfile, 'https://github.com/jprichardson/node-fs-extra/pull/141', err => {
if (err) return callback(err)
fs.open(tmpfile, 'r+', (err, fd) => {
if (err) return callback(err)
fs.futimes(fd, d, d, err => {
if (err) return callback(err)
fs.close(fd, err => {
if (err) return callback(err)
fs.stat(tmpfile, (err, stats) => {
if (err) return callback(err)
callback(null, stats.mtime > 1435410243000)
})
})
})
})
})
}
function timeRemoveMillis (timestamp) {
if (typeof timestamp === 'number') {
return Math.floor(timestamp / 1000) * 1000
} else if (timestamp instanceof Date) {
return new Date(Math.floor(timestamp.getTime() / 1000) * 1000)
} else {
throw new Error('fs-extra: timeRemoveMillis() unknown parameter type')
}
}
function utimesMillis (path, atime, mtime, callback) {
// if (!HAS_MILLIS_RES) return fs.utimes(path, atime, mtime, callback)
fs.open(path, 'r+', (err, fd) => {
if (err) return callback(err)
fs.futimes(fd, atime, mtime, futimesErr => {
fs.close(fd, closeErr => {
if (callback) callback(futimesErr || closeErr)
})
})
})
}
function utimesMillisSync (path, atime, mtime) {
const fd = fs.openSync(path, 'r+')
fs.futimesSync(fd, atime, mtime)
return fs.closeSync(fd)
}
module.exports = {
hasMillisRes,
hasMillisResSync,
timeRemoveMillis,
utimesMillis,
utimesMillisSync
}
+100
View File
@@ -0,0 +1,100 @@
{
"_from": "fs-extra@^8.1.0",
"_id": "fs-extra@8.1.0",
"_inBundle": false,
"_integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
"_location": "/workbox-build/fs-extra",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "fs-extra@^8.1.0",
"name": "fs-extra",
"escapedName": "fs-extra",
"rawSpec": "^8.1.0",
"saveSpec": null,
"fetchSpec": "^8.1.0"
},
"_requiredBy": [
"/workbox-build"
],
"_resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
"_shasum": "49d43c45a88cd9677668cb7be1b46efdb8d2e1c0",
"_spec": "fs-extra@^8.1.0",
"_where": "/Users/tylerkoenig/Code/personal/react-scss2/node_modules/workbox-build",
"author": {
"name": "JP Richardson",
"email": "jprichardson@gmail.com"
},
"bugs": {
"url": "https://github.com/jprichardson/node-fs-extra/issues"
},
"bundleDependencies": false,
"dependencies": {
"graceful-fs": "^4.2.0",
"jsonfile": "^4.0.0",
"universalify": "^0.1.0"
},
"deprecated": false,
"description": "fs-extra contains methods that aren't included in the vanilla Node.js fs package. Such as mkdir -p, cp -r, and rm -rf.",
"devDependencies": {
"coveralls": "^3.0.0",
"istanbul": "^0.4.5",
"klaw": "^2.1.1",
"klaw-sync": "^3.0.2",
"minimist": "^1.1.1",
"mocha": "^5.0.5",
"proxyquire": "^2.0.1",
"read-dir-files": "^0.1.1",
"semver": "^5.3.0",
"standard": "^12.0.1"
},
"engines": {
"node": ">=6 <7 || >=8"
},
"files": [
"lib/",
"!lib/**/__tests__/"
],
"homepage": "https://github.com/jprichardson/node-fs-extra",
"keywords": [
"fs",
"file",
"file system",
"copy",
"directory",
"extra",
"mkdirp",
"mkdir",
"mkdirs",
"recursive",
"json",
"read",
"write",
"extra",
"delete",
"remove",
"touch",
"create",
"text",
"output",
"move"
],
"license": "MIT",
"main": "./lib/index.js",
"name": "fs-extra",
"repository": {
"type": "git",
"url": "git+https://github.com/jprichardson/node-fs-extra.git"
},
"scripts": {
"coverage": "istanbul cover -i 'lib/**' -x '**/__tests__/**' test.js",
"coveralls": "coveralls < coverage/lcov.info",
"full-ci": "npm run lint && npm run coverage",
"lint": "standard",
"test": "npm run lint && npm run unit",
"test-find": "find ./lib/**/__tests__ -name *.test.js | xargs mocha",
"unit": "node test.js"
},
"version": "8.1.0"
}
+145
View File
@@ -0,0 +1,145 @@
4.0.0 / 2017-07-12
------------------
- **BREAKING:** Remove global `spaces` option.
- **BREAKING:** Drop support for Node 0.10, 0.12, and io.js.
- Remove undocumented `passParsingErrors` option.
- Added `EOL` override option to `writeFile` when using `spaces`. [#89]
3.0.1 / 2017-07-05
------------------
- Fixed bug in `writeFile` when there was a serialization error & no callback was passed. In previous versions, an empty file would be written; now no file is written.
3.0.0 / 2017-04-25
------------------
- Changed behavior of `throws` option for `readFileSync`; now does not throw filesystem errors when `throws` is `false`
2.4.0 / 2016-09-15
------------------
### Changed
- added optional support for `graceful-fs` [#62]
2.3.1 / 2016-05-13
------------------
- fix to support BOM. [#45][#45]
2.3.0 / 2016-04-16
------------------
- add `throws` to `readFile()`. See [#39][#39]
- add support for any arbitrary `fs` module. Useful with [mock-fs](https://www.npmjs.com/package/mock-fs)
2.2.3 / 2015-10-14
------------------
- include file name in parse error. See: https://github.com/jprichardson/node-jsonfile/pull/34
2.2.2 / 2015-09-16
------------------
- split out tests into separate files
- fixed `throws` when set to `true` in `readFileSync()`. See: https://github.com/jprichardson/node-jsonfile/pull/33
2.2.1 / 2015-06-25
------------------
- fixed regression when passing in string as encoding for options in `writeFile()` and `writeFileSync()`. See: https://github.com/jprichardson/node-jsonfile/issues/28
2.2.0 / 2015-06-25
------------------
- added `options.spaces` to `writeFile()` and `writeFileSync()`
2.1.2 / 2015-06-22
------------------
- fixed if passed `readFileSync(file, 'utf8')`. See: https://github.com/jprichardson/node-jsonfile/issues/25
2.1.1 / 2015-06-19
------------------
- fixed regressions if `null` is passed for options. See: https://github.com/jprichardson/node-jsonfile/issues/24
2.1.0 / 2015-06-19
------------------
- cleanup: JavaScript Standard Style, rename files, dropped terst for assert
- methods now support JSON revivers/replacers
2.0.1 / 2015-05-24
------------------
- update license attribute https://github.com/jprichardson/node-jsonfile/pull/21
2.0.0 / 2014-07-28
------------------
* added `\n` to end of file on write. [#14](https://github.com/jprichardson/node-jsonfile/pull/14)
* added `options.throws` to `readFileSync()`
* dropped support for Node v0.8
1.2.0 / 2014-06-29
------------------
* removed semicolons
* bugfix: passed `options` to `fs.readFile` and `fs.readFileSync`. This technically changes behavior, but
changes it according to docs. [#12][#12]
1.1.1 / 2013-11-11
------------------
* fixed catching of callback bug (ffissore / #5)
1.1.0 / 2013-10-11
------------------
* added `options` param to methods, (seanodell / #4)
1.0.1 / 2013-09-05
------------------
* removed `homepage` field from package.json to remove NPM warning
1.0.0 / 2013-06-28
------------------
* added `.npmignore`, #1
* changed spacing default from `4` to `2` to follow Node conventions
0.0.1 / 2012-09-10
------------------
* Initial release.
[#89]: https://github.com/jprichardson/node-jsonfile/pull/89
[#45]: https://github.com/jprichardson/node-jsonfile/issues/45 "Reading of UTF8-encoded (w/ BOM) files fails"
[#44]: https://github.com/jprichardson/node-jsonfile/issues/44 "Extra characters in written file"
[#43]: https://github.com/jprichardson/node-jsonfile/issues/43 "Prettyfy json when written to file"
[#42]: https://github.com/jprichardson/node-jsonfile/pull/42 "Moved fs.readFileSync within the try/catch"
[#41]: https://github.com/jprichardson/node-jsonfile/issues/41 "Linux: Hidden file not working"
[#40]: https://github.com/jprichardson/node-jsonfile/issues/40 "autocreate folder doesn't work from Path-value"
[#39]: https://github.com/jprichardson/node-jsonfile/pull/39 "Add `throws` option for readFile (async)"
[#38]: https://github.com/jprichardson/node-jsonfile/pull/38 "Update README.md writeFile[Sync] signature"
[#37]: https://github.com/jprichardson/node-jsonfile/pull/37 "support append file"
[#36]: https://github.com/jprichardson/node-jsonfile/pull/36 "Add typescript definition file."
[#35]: https://github.com/jprichardson/node-jsonfile/pull/35 "Add typescript definition file."
[#34]: https://github.com/jprichardson/node-jsonfile/pull/34 "readFile JSON parse error includes filename"
[#33]: https://github.com/jprichardson/node-jsonfile/pull/33 "fix throw->throws typo in readFileSync()"
[#32]: https://github.com/jprichardson/node-jsonfile/issues/32 "readFile & readFileSync can possible have strip-comments as an option?"
[#31]: https://github.com/jprichardson/node-jsonfile/pull/31 "[Modify] Support string include is unicode escape string"
[#30]: https://github.com/jprichardson/node-jsonfile/issues/30 "How to use Jsonfile package in Meteor.js App?"
[#29]: https://github.com/jprichardson/node-jsonfile/issues/29 "writefile callback if no error?"
[#28]: https://github.com/jprichardson/node-jsonfile/issues/28 "writeFile options argument broken "
[#27]: https://github.com/jprichardson/node-jsonfile/pull/27 "Use svg instead of png to get better image quality"
[#26]: https://github.com/jprichardson/node-jsonfile/issues/26 "Breaking change to fs-extra"
[#25]: https://github.com/jprichardson/node-jsonfile/issues/25 "support string encoding param for read methods"
[#24]: https://github.com/jprichardson/node-jsonfile/issues/24 "readFile: Passing in null options with a callback throws an error"
[#23]: https://github.com/jprichardson/node-jsonfile/pull/23 "Add appendFile and appendFileSync"
[#22]: https://github.com/jprichardson/node-jsonfile/issues/22 "Default value for spaces in readme.md is outdated"
[#21]: https://github.com/jprichardson/node-jsonfile/pull/21 "Update license attribute"
[#20]: https://github.com/jprichardson/node-jsonfile/issues/20 "Add simple caching functionallity"
[#19]: https://github.com/jprichardson/node-jsonfile/pull/19 "Add appendFileSync method"
[#18]: https://github.com/jprichardson/node-jsonfile/issues/18 "Add updateFile and updateFileSync methods"
[#17]: https://github.com/jprichardson/node-jsonfile/issues/17 "seem read & write sync has sequentially problem"
[#16]: https://github.com/jprichardson/node-jsonfile/pull/16 "export spaces defaulted to null"
[#15]: https://github.com/jprichardson/node-jsonfile/issues/15 "`jsonfile.spaces` should default to `null`"
[#14]: https://github.com/jprichardson/node-jsonfile/pull/14 "Add EOL at EOF"
[#13]: https://github.com/jprichardson/node-jsonfile/issues/13 "Add a final newline"
[#12]: https://github.com/jprichardson/node-jsonfile/issues/12 "readFile doesn't accept options"
[#11]: https://github.com/jprichardson/node-jsonfile/pull/11 "Added try,catch to readFileSync"
[#10]: https://github.com/jprichardson/node-jsonfile/issues/10 "No output or error from writeFile"
[#9]: https://github.com/jprichardson/node-jsonfile/pull/9 "Change 'js' to 'jf' in example."
[#8]: https://github.com/jprichardson/node-jsonfile/pull/8 "Updated forgotten module.exports to me."
[#7]: https://github.com/jprichardson/node-jsonfile/pull/7 "Add file name in error message"
[#6]: https://github.com/jprichardson/node-jsonfile/pull/6 "Use graceful-fs when possible"
[#5]: https://github.com/jprichardson/node-jsonfile/pull/5 "Jsonfile doesn't behave nicely when used inside a test suite."
[#4]: https://github.com/jprichardson/node-jsonfile/pull/4 "Added options parameter to writeFile and writeFileSync"
[#3]: https://github.com/jprichardson/node-jsonfile/issues/3 "test2"
[#2]: https://github.com/jprichardson/node-jsonfile/issues/2 "homepage field must be a string url. Deleted."
[#1]: https://github.com/jprichardson/node-jsonfile/pull/1 "adding an `.npmignore` file"
+15
View File
@@ -0,0 +1,15 @@
(The MIT License)
Copyright (c) 2012-2015, JP Richardson <jprichardson@gmail.com>
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.
+168
View File
@@ -0,0 +1,168 @@
Node.js - jsonfile
================
Easily read/write JSON files.
[![npm Package](https://img.shields.io/npm/v/jsonfile.svg?style=flat-square)](https://www.npmjs.org/package/jsonfile)
[![build status](https://secure.travis-ci.org/jprichardson/node-jsonfile.svg)](http://travis-ci.org/jprichardson/node-jsonfile)
[![windows Build status](https://img.shields.io/appveyor/ci/jprichardson/node-jsonfile/master.svg?label=windows%20build)](https://ci.appveyor.com/project/jprichardson/node-jsonfile/branch/master)
<a href="https://github.com/feross/standard"><img src="https://cdn.rawgit.com/feross/standard/master/sticker.svg" alt="Standard JavaScript" width="100"></a>
Why?
----
Writing `JSON.stringify()` and then `fs.writeFile()` and `JSON.parse()` with `fs.readFile()` enclosed in `try/catch` blocks became annoying.
Installation
------------
npm install --save jsonfile
API
---
### readFile(filename, [options], callback)
`options` (`object`, default `undefined`): Pass in any `fs.readFile` options or set `reviver` for a [JSON reviver](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse).
- `throws` (`boolean`, default: `true`). If `JSON.parse` throws an error, pass this error to the callback.
If `false`, returns `null` for the object.
```js
var jsonfile = require('jsonfile')
var file = '/tmp/data.json'
jsonfile.readFile(file, function(err, obj) {
console.dir(obj)
})
```
### readFileSync(filename, [options])
`options` (`object`, default `undefined`): Pass in any `fs.readFileSync` options or set `reviver` for a [JSON reviver](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse).
- `throws` (`boolean`, default: `true`). If an error is encountered reading or parsing the file, throw the error. If `false`, returns `null` for the object.
```js
var jsonfile = require('jsonfile')
var file = '/tmp/data.json'
console.dir(jsonfile.readFileSync(file))
```
### writeFile(filename, obj, [options], callback)
`options`: Pass in any `fs.writeFile` options or set `replacer` for a [JSON replacer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify). Can also pass in `spaces` and override `EOL` string.
```js
var jsonfile = require('jsonfile')
var file = '/tmp/data.json'
var obj = {name: 'JP'}
jsonfile.writeFile(file, obj, function (err) {
console.error(err)
})
```
**formatting with spaces:**
```js
var jsonfile = require('jsonfile')
var file = '/tmp/data.json'
var obj = {name: 'JP'}
jsonfile.writeFile(file, obj, {spaces: 2}, function(err) {
console.error(err)
})
```
**overriding EOL:**
```js
var jsonfile = require('jsonfile')
var file = '/tmp/data.json'
var obj = {name: 'JP'}
jsonfile.writeFile(file, obj, {spaces: 2, EOL: '\r\n'}, function(err) {
console.error(err)
})
```
**appending to an existing JSON file:**
You can use `fs.writeFile` option `{flag: 'a'}` to achieve this.
```js
var jsonfile = require('jsonfile')
var file = '/tmp/mayAlreadyExistedData.json'
var obj = {name: 'JP'}
jsonfile.writeFile(file, obj, {flag: 'a'}, function (err) {
console.error(err)
})
```
### writeFileSync(filename, obj, [options])
`options`: Pass in any `fs.writeFileSync` options or set `replacer` for a [JSON replacer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify). Can also pass in `spaces` and override `EOL` string.
```js
var jsonfile = require('jsonfile')
var file = '/tmp/data.json'
var obj = {name: 'JP'}
jsonfile.writeFileSync(file, obj)
```
**formatting with spaces:**
```js
var jsonfile = require('jsonfile')
var file = '/tmp/data.json'
var obj = {name: 'JP'}
jsonfile.writeFileSync(file, obj, {spaces: 2})
```
**overriding EOL:**
```js
var jsonfile = require('jsonfile')
var file = '/tmp/data.json'
var obj = {name: 'JP'}
jsonfile.writeFileSync(file, obj, {spaces: 2, EOL: '\r\n'})
```
**appending to an existing JSON file:**
You can use `fs.writeFileSync` option `{flag: 'a'}` to achieve this.
```js
var jsonfile = require('jsonfile')
var file = '/tmp/mayAlreadyExistedData.json'
var obj = {name: 'JP'}
jsonfile.writeFileSync(file, obj, {flag: 'a'})
```
License
-------
(MIT License)
Copyright 2012-2016, JP Richardson <jprichardson@gmail.com>
+134
View File
@@ -0,0 +1,134 @@
var _fs
try {
_fs = require('graceful-fs')
} catch (_) {
_fs = require('fs')
}
function readFile (file, options, callback) {
if (callback == null) {
callback = options
options = {}
}
if (typeof options === 'string') {
options = {encoding: options}
}
options = options || {}
var fs = options.fs || _fs
var shouldThrow = true
if ('throws' in options) {
shouldThrow = options.throws
}
fs.readFile(file, options, function (err, data) {
if (err) return callback(err)
data = stripBom(data)
var obj
try {
obj = JSON.parse(data, options ? options.reviver : null)
} catch (err2) {
if (shouldThrow) {
err2.message = file + ': ' + err2.message
return callback(err2)
} else {
return callback(null, null)
}
}
callback(null, obj)
})
}
function readFileSync (file, options) {
options = options || {}
if (typeof options === 'string') {
options = {encoding: options}
}
var fs = options.fs || _fs
var shouldThrow = true
if ('throws' in options) {
shouldThrow = options.throws
}
try {
var content = fs.readFileSync(file, options)
content = stripBom(content)
return JSON.parse(content, options.reviver)
} catch (err) {
if (shouldThrow) {
err.message = file + ': ' + err.message
throw err
} else {
return null
}
}
}
function stringify (obj, options) {
var spaces
var EOL = '\n'
if (typeof options === 'object' && options !== null) {
if (options.spaces) {
spaces = options.spaces
}
if (options.EOL) {
EOL = options.EOL
}
}
var str = JSON.stringify(obj, options ? options.replacer : null, spaces)
return str.replace(/\n/g, EOL) + EOL
}
function writeFile (file, obj, options, callback) {
if (callback == null) {
callback = options
options = {}
}
options = options || {}
var fs = options.fs || _fs
var str = ''
try {
str = stringify(obj, options)
} catch (err) {
// Need to return whether a callback was passed or not
if (callback) callback(err, null)
return
}
fs.writeFile(file, str, options, callback)
}
function writeFileSync (file, obj, options) {
options = options || {}
var fs = options.fs || _fs
var str = stringify(obj, options)
// not sure if fs.writeFileSync returns anything, but just in case
return fs.writeFileSync(file, str, options)
}
function stripBom (content) {
// we do this because JSON.parse would convert it to a utf8 string if encoding wasn't specified
if (Buffer.isBuffer(content)) content = content.toString('utf8')
content = content.replace(/^\uFEFF/, '')
return content
}
var jsonfile = {
readFile: readFile,
readFileSync: readFileSync,
writeFile: writeFile,
writeFileSync: writeFileSync
}
module.exports = jsonfile
+71
View File
@@ -0,0 +1,71 @@
{
"_from": "jsonfile@^4.0.0",
"_id": "jsonfile@4.0.0",
"_inBundle": false,
"_integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=",
"_location": "/workbox-build/jsonfile",
"_phantomChildren": {},
"_requested": {
"type": "range",
"registry": true,
"raw": "jsonfile@^4.0.0",
"name": "jsonfile",
"escapedName": "jsonfile",
"rawSpec": "^4.0.0",
"saveSpec": null,
"fetchSpec": "^4.0.0"
},
"_requiredBy": [
"/workbox-build/fs-extra"
],
"_resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
"_shasum": "8771aae0799b64076b76640fca058f9c10e33ecb",
"_spec": "jsonfile@^4.0.0",
"_where": "/Users/tylerkoenig/Code/personal/react-scss2/node_modules/workbox-build/node_modules/fs-extra",
"author": {
"name": "JP Richardson",
"email": "jprichardson@gmail.com"
},
"bugs": {
"url": "https://github.com/jprichardson/node-jsonfile/issues"
},
"bundleDependencies": false,
"dependencies": {
"graceful-fs": "^4.1.6"
},
"deprecated": false,
"description": "Easily read/write JSON files.",
"devDependencies": {
"mocha": "2.x",
"rimraf": "^2.4.0",
"standard": "^10.0.3"
},
"files": [
"index.js"
],
"homepage": "https://github.com/jprichardson/node-jsonfile#readme",
"keywords": [
"read",
"write",
"file",
"json",
"fs",
"fs-extra"
],
"license": "MIT",
"main": "index.js",
"name": "jsonfile",
"optionalDependencies": {
"graceful-fs": "^4.1.6"
},
"repository": {
"type": "git",
"url": "git+ssh://git@github.com/jprichardson/node-jsonfile.git"
},
"scripts": {
"lint": "standard",
"test": "npm run lint && npm run unit",
"unit": "mocha"
},
"version": "4.0.0"
}
+344
View File
@@ -0,0 +1,344 @@
# Change Log
## 0.7.3
* Fix a bug where nested uses of `SourceMapConsumer` could result in a
`TypeError`. [#338](https://github.com/mozilla/source-map/issues/338)
[#330](https://github.com/mozilla/source-map/issues/330)
[#319](https://github.com/mozilla/source-map/issues/319)
## 0.7.2
* Another 3x speed up in `SourceMapConsumer`. Read about it here:
http://fitzgeraldnick.com/2018/02/26/speed-without-wizardry.html
## 0.7.1
* Updated TypeScript typings. [#321][]
[#321]: https://github.com/mozilla/source-map/pull/321
## 0.7.0
* `SourceMapConsumer` now uses WebAssembly, and is **much** faster! Read about
it here:
https://hacks.mozilla.org/2018/01/oxidizing-source-maps-with-rust-and-webassembly/
* **Breaking change:** `new SourceMapConsumer` now returns a `Promise` object
that resolves to the newly constructed `SourceMapConsumer` instance, rather
than returning the new instance immediately.
* **Breaking change:** when you're done using a `SourceMapConsumer` instance,
you must call `SourceMapConsumer.prototype.destroy` on it. After calling
`destroy`, you must not use the instance again.
* **Breaking change:** `SourceMapConsumer` used to be able to handle lines,
columns numbers and source and name indices up to `2^53 - 1` (aka
`Number.MAX_SAFE_INTEGER`). It can now only handle them up to `2^32 - 1`.
* **Breaking change:** The `source-map` library now uses modern ECMAScript-isms:
`let`, arrow functions, `async`, etc. Use Babel to compile it down to
ECMAScript 5 if you need to support older JavaScript environments.
* **Breaking change:** Drop support for Node < 8. If you want to support older
versions of node, please use v0.6 or below.
## 0.5.6
* Fix for regression when people were using numbers as names in source maps. See
#236.
## 0.5.5
* Fix "regression" of unsupported, implementation behavior that half the world
happens to have come to depend on. See #235.
* Fix regression involving function hoisting in SpiderMonkey. See #233.
## 0.5.4
* Large performance improvements to source-map serialization. See #228 and #229.
## 0.5.3
* Do not include unnecessary distribution files. See
commit ef7006f8d1647e0a83fdc60f04f5a7ca54886f86.
## 0.5.2
* Include browser distributions of the library in package.json's `files`. See
issue #212.
## 0.5.1
* Fix latent bugs in IndexedSourceMapConsumer.prototype._parseMappings. See
ff05274becc9e6e1295ed60f3ea090d31d843379.
## 0.5.0
* Node 0.8 is no longer supported.
* Use webpack instead of dryice for bundling.
* Big speedups serializing source maps. See pull request #203.
* Fix a bug with `SourceMapConsumer.prototype.sourceContentFor` and sources that
explicitly start with the source root. See issue #199.
## 0.4.4
* Fix an issue where using a `SourceMapGenerator` after having created a
`SourceMapConsumer` from it via `SourceMapConsumer.fromSourceMap` failed. See
issue #191.
* Fix an issue with where `SourceMapGenerator` would mistakenly consider
different mappings as duplicates of each other and avoid generating them. See
issue #192.
## 0.4.3
* A very large number of performance improvements, particularly when parsing
source maps. Collectively about 75% of time shaved off of the source map
parsing benchmark!
* Fix a bug in `SourceMapConsumer.prototype.allGeneratedPositionsFor` and fuzzy
searching in the presence of a column option. See issue #177.
* Fix a bug with joining a source and its source root when the source is above
the root. See issue #182.
* Add the `SourceMapConsumer.prototype.hasContentsOfAllSources` method to
determine when all sources' contents are inlined into the source map. See
issue #190.
## 0.4.2
* Add an `.npmignore` file so that the benchmarks aren't pulled down by
dependent projects. Issue #169.
* Add an optional `column` argument to
`SourceMapConsumer.prototype.allGeneratedPositionsFor` and better handle lines
with no mappings. Issues #172 and #173.
## 0.4.1
* Fix accidentally defining a global variable. #170.
## 0.4.0
* The default direction for fuzzy searching was changed back to its original
direction. See #164.
* There is now a `bias` option you can supply to `SourceMapConsumer` to control
the fuzzy searching direction. See #167.
* About an 8% speed up in parsing source maps. See #159.
* Added a benchmark for parsing and generating source maps.
## 0.3.0
* Change the default direction that searching for positions fuzzes when there is
not an exact match. See #154.
* Support for environments using json2.js for JSON serialization. See #156.
## 0.2.0
* Support for consuming "indexed" source maps which do not have any remote
sections. See pull request #127. This introduces a minor backwards
incompatibility if you are monkey patching `SourceMapConsumer.prototype`
methods.
## 0.1.43
* Performance improvements for `SourceMapGenerator` and `SourceNode`. See issue
#148 for some discussion and issues #150, #151, and #152 for implementations.
## 0.1.42
* Fix an issue where `SourceNode`s from different versions of the source-map
library couldn't be used in conjunction with each other. See issue #142.
## 0.1.41
* Fix a bug with getting the source content of relative sources with a "./"
prefix. See issue #145 and [Bug 1090768](bugzil.la/1090768).
* Add the `SourceMapConsumer.prototype.computeColumnSpans` method to compute the
column span of each mapping.
* Add the `SourceMapConsumer.prototype.allGeneratedPositionsFor` method to find
all generated positions associated with a given original source and line.
## 0.1.40
* Performance improvements for parsing source maps in SourceMapConsumer.
## 0.1.39
* Fix a bug where setting a source's contents to null before any source content
had been set before threw a TypeError. See issue #131.
## 0.1.38
* Fix a bug where finding relative paths from an empty path were creating
absolute paths. See issue #129.
## 0.1.37
* Fix a bug where if the source root was an empty string, relative source paths
would turn into absolute source paths. Issue #124.
## 0.1.36
* Allow the `names` mapping property to be an empty string. Issue #121.
## 0.1.35
* A third optional parameter was added to `SourceNode.fromStringWithSourceMap`
to specify a path that relative sources in the second parameter should be
relative to. Issue #105.
* If no file property is given to a `SourceMapGenerator`, then the resulting
source map will no longer have a `null` file property. The property will
simply not exist. Issue #104.
* Fixed a bug where consecutive newlines were ignored in `SourceNode`s.
Issue #116.
## 0.1.34
* Make `SourceNode` work with windows style ("\r\n") newlines. Issue #103.
* Fix bug involving source contents and the
`SourceMapGenerator.prototype.applySourceMap`. Issue #100.
## 0.1.33
* Fix some edge cases surrounding path joining and URL resolution.
* Add a third parameter for relative path to
`SourceMapGenerator.prototype.applySourceMap`.
* Fix issues with mappings and EOLs.
## 0.1.32
* Fixed a bug where SourceMapConsumer couldn't handle negative relative columns
(issue 92).
* Fixed test runner to actually report number of failed tests as its process
exit code.
* Fixed a typo when reporting bad mappings (issue 87).
## 0.1.31
* Delay parsing the mappings in SourceMapConsumer until queried for a source
location.
* Support Sass source maps (which at the time of writing deviate from the spec
in small ways) in SourceMapConsumer.
## 0.1.30
* Do not join source root with a source, when the source is a data URI.
* Extend the test runner to allow running single specific test files at a time.
* Performance improvements in `SourceNode.prototype.walk` and
`SourceMapConsumer.prototype.eachMapping`.
* Source map browser builds will now work inside Workers.
* Better error messages when attempting to add an invalid mapping to a
`SourceMapGenerator`.
## 0.1.29
* Allow duplicate entries in the `names` and `sources` arrays of source maps
(usually from TypeScript) we are parsing. Fixes github issue 72.
## 0.1.28
* Skip duplicate mappings when creating source maps from SourceNode; github
issue 75.
## 0.1.27
* Don't throw an error when the `file` property is missing in SourceMapConsumer,
we don't use it anyway.
## 0.1.26
* Fix SourceNode.fromStringWithSourceMap for empty maps. Fixes github issue 70.
## 0.1.25
* Make compatible with browserify
## 0.1.24
* Fix issue with absolute paths and `file://` URIs. See
https://bugzilla.mozilla.org/show_bug.cgi?id=885597
## 0.1.23
* Fix issue with absolute paths and sourcesContent, github issue 64.
## 0.1.22
* Ignore duplicate mappings in SourceMapGenerator. Fixes github issue 21.
## 0.1.21
* Fixed handling of sources that start with a slash so that they are relative to
the source root's host.
## 0.1.20
* Fixed github issue #43: absolute URLs aren't joined with the source root
anymore.
## 0.1.19
* Using Travis CI to run tests.
## 0.1.18
* Fixed a bug in the handling of sourceRoot.
## 0.1.17
* Added SourceNode.fromStringWithSourceMap.
## 0.1.16
* Added missing documentation.
* Fixed the generating of empty mappings in SourceNode.
## 0.1.15
* Added SourceMapGenerator.applySourceMap.
## 0.1.14
* The sourceRoot is now handled consistently.
## 0.1.13
* Added SourceMapGenerator.fromSourceMap.
## 0.1.12
* SourceNode now generates empty mappings too.
## 0.1.11
* Added name support to SourceNode.
## 0.1.10
* Added sourcesContent support to the customer and generator.
+28
View File
@@ -0,0 +1,28 @@
Copyright (c) 2009-2011, Mozilla Foundation and contributors
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the names of the Mozilla Foundation nor the names of project
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+822
View File
@@ -0,0 +1,822 @@
# Source Map
[![Build Status](https://travis-ci.org/mozilla/source-map.png?branch=master)](https://travis-ci.org/mozilla/source-map)
[![Coverage Status](https://coveralls.io/repos/github/mozilla/source-map/badge.svg)](https://coveralls.io/github/mozilla/source-map)
[![NPM](https://nodei.co/npm/source-map.png?downloads=true&downloadRank=true)](https://www.npmjs.com/package/source-map)
This is a library to generate and consume the source map format
[described here][format].
[format]: https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit
## Use with Node
$ npm install source-map
## Use on the Web
<script src="https://unpkg.com/source-map@0.7.3/dist/source-map.js"></script>
<script>
sourceMap.SourceMapConsumer.initialize({
"lib/mappings.wasm": "https://unpkg.com/source-map@0.7.3/lib/mappings.wasm"
});
</script>
--------------------------------------------------------------------------------
<!-- `npm run toc` to regenerate the Table of Contents -->
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
## Table of Contents
- [Examples](#examples)
- [Consuming a source map](#consuming-a-source-map)
- [Generating a source map](#generating-a-source-map)
- [With SourceNode (high level API)](#with-sourcenode-high-level-api)
- [With SourceMapGenerator (low level API)](#with-sourcemapgenerator-low-level-api)
- [API](#api)
- [SourceMapConsumer](#sourcemapconsumer)
- [SourceMapConsumer.initialize(options)](#sourcemapconsumerinitializeoptions)
- [new SourceMapConsumer(rawSourceMap)](#new-sourcemapconsumerrawsourcemap)
- [SourceMapConsumer.with](#sourcemapconsumerwith)
- [SourceMapConsumer.prototype.destroy()](#sourcemapconsumerprototypedestroy)
- [SourceMapConsumer.prototype.computeColumnSpans()](#sourcemapconsumerprototypecomputecolumnspans)
- [SourceMapConsumer.prototype.originalPositionFor(generatedPosition)](#sourcemapconsumerprototypeoriginalpositionforgeneratedposition)
- [SourceMapConsumer.prototype.generatedPositionFor(originalPosition)](#sourcemapconsumerprototypegeneratedpositionfororiginalposition)
- [SourceMapConsumer.prototype.allGeneratedPositionsFor(originalPosition)](#sourcemapconsumerprototypeallgeneratedpositionsfororiginalposition)
- [SourceMapConsumer.prototype.hasContentsOfAllSources()](#sourcemapconsumerprototypehascontentsofallsources)
- [SourceMapConsumer.prototype.sourceContentFor(source[, returnNullOnMissing])](#sourcemapconsumerprototypesourcecontentforsource-returnnullonmissing)
- [SourceMapConsumer.prototype.eachMapping(callback, context, order)](#sourcemapconsumerprototypeeachmappingcallback-context-order)
- [SourceMapGenerator](#sourcemapgenerator)
- [new SourceMapGenerator([startOfSourceMap])](#new-sourcemapgeneratorstartofsourcemap)
- [SourceMapGenerator.fromSourceMap(sourceMapConsumer)](#sourcemapgeneratorfromsourcemapsourcemapconsumer)
- [SourceMapGenerator.prototype.addMapping(mapping)](#sourcemapgeneratorprototypeaddmappingmapping)
- [SourceMapGenerator.prototype.setSourceContent(sourceFile, sourceContent)](#sourcemapgeneratorprototypesetsourcecontentsourcefile-sourcecontent)
- [SourceMapGenerator.prototype.applySourceMap(sourceMapConsumer[, sourceFile[, sourceMapPath]])](#sourcemapgeneratorprototypeapplysourcemapsourcemapconsumer-sourcefile-sourcemappath)
- [SourceMapGenerator.prototype.toString()](#sourcemapgeneratorprototypetostring)
- [SourceNode](#sourcenode)
- [new SourceNode([line, column, source[, chunk[, name]]])](#new-sourcenodeline-column-source-chunk-name)
- [SourceNode.fromStringWithSourceMap(code, sourceMapConsumer[, relativePath])](#sourcenodefromstringwithsourcemapcode-sourcemapconsumer-relativepath)
- [SourceNode.prototype.add(chunk)](#sourcenodeprototypeaddchunk)
- [SourceNode.prototype.prepend(chunk)](#sourcenodeprototypeprependchunk)
- [SourceNode.prototype.setSourceContent(sourceFile, sourceContent)](#sourcenodeprototypesetsourcecontentsourcefile-sourcecontent)
- [SourceNode.prototype.walk(fn)](#sourcenodeprototypewalkfn)
- [SourceNode.prototype.walkSourceContents(fn)](#sourcenodeprototypewalksourcecontentsfn)
- [SourceNode.prototype.join(sep)](#sourcenodeprototypejoinsep)
- [SourceNode.prototype.replaceRight(pattern, replacement)](#sourcenodeprototypereplacerightpattern-replacement)
- [SourceNode.prototype.toString()](#sourcenodeprototypetostring)
- [SourceNode.prototype.toStringWithSourceMap([startOfSourceMap])](#sourcenodeprototypetostringwithsourcemapstartofsourcemap)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
## Examples
### Consuming a source map
```js
const rawSourceMap = {
version: 3,
file: 'min.js',
names: ['bar', 'baz', 'n'],
sources: ['one.js', 'two.js'],
sourceRoot: 'http://example.com/www/js/',
mappings: 'CAAC,IAAI,IAAM,SAAUA,GAClB,OAAOC,IAAID;CCDb,IAAI,IAAM,SAAUE,GAClB,OAAOA'
};
const whatever = await SourceMapConsumer.with(rawSourceMap, null, consumer => {
console.log(consumer.sources);
// [ 'http://example.com/www/js/one.js',
// 'http://example.com/www/js/two.js' ]
console.log(consumer.originalPositionFor({
line: 2,
column: 28
}));
// { source: 'http://example.com/www/js/two.js',
// line: 2,
// column: 10,
// name: 'n' }
console.log(consumer.generatedPositionFor({
source: 'http://example.com/www/js/two.js',
line: 2,
column: 10
}));
// { line: 2, column: 28 }
consumer.eachMapping(function (m) {
// ...
});
return computeWhatever();
});
```
### Generating a source map
In depth guide:
[**Compiling to JavaScript, and Debugging with Source Maps**](https://hacks.mozilla.org/2013/05/compiling-to-javascript-and-debugging-with-source-maps/)
#### With SourceNode (high level API)
```js
function compile(ast) {
switch (ast.type) {
case 'BinaryExpression':
return new SourceNode(
ast.location.line,
ast.location.column,
ast.location.source,
[compile(ast.left), " + ", compile(ast.right)]
);
case 'Literal':
return new SourceNode(
ast.location.line,
ast.location.column,
ast.location.source,
String(ast.value)
);
// ...
default:
throw new Error("Bad AST");
}
}
var ast = parse("40 + 2", "add.js");
console.log(compile(ast).toStringWithSourceMap({
file: 'add.js'
}));
// { code: '40 + 2',
// map: [object SourceMapGenerator] }
```
#### With SourceMapGenerator (low level API)
```js
var map = new SourceMapGenerator({
file: "source-mapped.js"
});
map.addMapping({
generated: {
line: 10,
column: 35
},
source: "foo.js",
original: {
line: 33,
column: 2
},
name: "christopher"
});
console.log(map.toString());
// '{"version":3,"file":"source-mapped.js","sources":["foo.js"],"names":["christopher"],"mappings":";;;;;;;;;mCAgCEA"}'
```
## API
Get a reference to the module:
```js
// Node.js
var sourceMap = require('source-map');
// Browser builds
var sourceMap = window.sourceMap;
// Inside Firefox
const sourceMap = require("devtools/toolkit/sourcemap/source-map.js");
```
### SourceMapConsumer
A `SourceMapConsumer` instance represents a parsed source map which we can query
for information about the original file positions by giving it a file position
in the generated source.
#### SourceMapConsumer.initialize(options)
When using `SourceMapConsumer` outside of node.js, for example on the Web, it
needs to know from what URL to load `lib/mappings.wasm`. You must inform it by
calling `initialize` before constructing any `SourceMapConsumer`s.
The options object has the following properties:
* `"lib/mappings.wasm"`: A `String` containing the URL of the
`lib/mappings.wasm` file.
```js
sourceMap.SourceMapConsumer.initialize({
"lib/mappings.wasm": "https://example.com/source-map/lib/mappings.wasm"
});
```
#### new SourceMapConsumer(rawSourceMap)
The only parameter is the raw source map (either as a string which can be
`JSON.parse`'d, or an object). According to the spec, source maps have the
following attributes:
* `version`: Which version of the source map spec this map is following.
* `sources`: An array of URLs to the original source files.
* `names`: An array of identifiers which can be referenced by individual
mappings.
* `sourceRoot`: Optional. The URL root from which all sources are relative.
* `sourcesContent`: Optional. An array of contents of the original source files.
* `mappings`: A string of base64 VLQs which contain the actual mappings.
* `file`: Optional. The generated filename this source map is associated with.
The promise of the constructed souce map consumer is returned.
When the `SourceMapConsumer` will no longer be used anymore, you must call its
`destroy` method.
```js
const consumer = await new sourceMap.SourceMapConsumer(rawSourceMapJsonData);
doStuffWith(consumer);
consumer.destroy();
```
Alternatively, you can use `SourceMapConsumer.with` to avoid needing to remember
to call `destroy`.
#### SourceMapConsumer.with
Construct a new `SourceMapConsumer` from `rawSourceMap` and `sourceMapUrl`
(see the `SourceMapConsumer` constructor for details. Then, invoke the `async
function f(SourceMapConsumer) -> T` with the newly constructed consumer, wait
for `f` to complete, call `destroy` on the consumer, and return `f`'s return
value.
You must not use the consumer after `f` completes!
By using `with`, you do not have to remember to manually call `destroy` on
the consumer, since it will be called automatically once `f` completes.
```js
const xSquared = await SourceMapConsumer.with(
myRawSourceMap,
null,
async function (consumer) {
// Use `consumer` inside here and don't worry about remembering
// to call `destroy`.
const x = await whatever(consumer);
return x * x;
}
);
// You may not use that `consumer` anymore out here; it has
// been destroyed. But you can use `xSquared`.
console.log(xSquared);
```
#### SourceMapConsumer.prototype.destroy()
Free this source map consumer's associated wasm data that is manually-managed.
```js
consumer.destroy();
```
Alternatively, you can use `SourceMapConsumer.with` to avoid needing to remember
to call `destroy`.
#### SourceMapConsumer.prototype.computeColumnSpans()
Compute the last column for each generated mapping. The last column is
inclusive.
```js
// Before:
consumer.allGeneratedPositionsFor({ line: 2, source: "foo.coffee" })
// [ { line: 2,
// column: 1 },
// { line: 2,
// column: 10 },
// { line: 2,
// column: 20 } ]
consumer.computeColumnSpans();
// After:
consumer.allGeneratedPositionsFor({ line: 2, source: "foo.coffee" })
// [ { line: 2,
// column: 1,
// lastColumn: 9 },
// { line: 2,
// column: 10,
// lastColumn: 19 },
// { line: 2,
// column: 20,
// lastColumn: Infinity } ]
```
#### SourceMapConsumer.prototype.originalPositionFor(generatedPosition)
Returns the original source, line, and column information for the generated
source's line and column positions provided. The only argument is an object with
the following properties:
* `line`: The line number in the generated source. Line numbers in
this library are 1-based (note that the underlying source map
specification uses 0-based line numbers -- this library handles the
translation).
* `column`: The column number in the generated source. Column numbers
in this library are 0-based.
* `bias`: Either `SourceMapConsumer.GREATEST_LOWER_BOUND` or
`SourceMapConsumer.LEAST_UPPER_BOUND`. Specifies whether to return the closest
element that is smaller than or greater than the one we are searching for,
respectively, if the exact element cannot be found. Defaults to
`SourceMapConsumer.GREATEST_LOWER_BOUND`.
and an object is returned with the following properties:
* `source`: The original source file, or null if this information is not
available.
* `line`: The line number in the original source, or null if this information is
not available. The line number is 1-based.
* `column`: The column number in the original source, or null if this
information is not available. The column number is 0-based.
* `name`: The original identifier, or null if this information is not available.
```js
consumer.originalPositionFor({ line: 2, column: 10 })
// { source: 'foo.coffee',
// line: 2,
// column: 2,
// name: null }
consumer.originalPositionFor({ line: 99999999999999999, column: 999999999999999 })
// { source: null,
// line: null,
// column: null,
// name: null }
```
#### SourceMapConsumer.prototype.generatedPositionFor(originalPosition)
Returns the generated line and column information for the original source,
line, and column positions provided. The only argument is an object with
the following properties:
* `source`: The filename of the original source.
* `line`: The line number in the original source. The line number is
1-based.
* `column`: The column number in the original source. The column
number is 0-based.
and an object is returned with the following properties:
* `line`: The line number in the generated source, or null. The line
number is 1-based.
* `column`: The column number in the generated source, or null. The
column number is 0-based.
```js
consumer.generatedPositionFor({ source: "example.js", line: 2, column: 10 })
// { line: 1,
// column: 56 }
```
#### SourceMapConsumer.prototype.allGeneratedPositionsFor(originalPosition)
Returns all generated line and column information for the original source, line,
and column provided. If no column is provided, returns all mappings
corresponding to a either the line we are searching for or the next closest line
that has any mappings. Otherwise, returns all mappings corresponding to the
given line and either the column we are searching for or the next closest column
that has any offsets.
The only argument is an object with the following properties:
* `source`: The filename of the original source.
* `line`: The line number in the original source. The line number is
1-based.
* `column`: Optional. The column number in the original source. The
column number is 0-based.
and an array of objects is returned, each with the following properties:
* `line`: The line number in the generated source, or null. The line
number is 1-based.
* `column`: The column number in the generated source, or null. The
column number is 0-based.
```js
consumer.allGeneratedpositionsfor({ line: 2, source: "foo.coffee" })
// [ { line: 2,
// column: 1 },
// { line: 2,
// column: 10 },
// { line: 2,
// column: 20 } ]
```
#### SourceMapConsumer.prototype.hasContentsOfAllSources()
Return true if we have the embedded source content for every source listed in
the source map, false otherwise.
In other words, if this method returns `true`, then
`consumer.sourceContentFor(s)` will succeed for every source `s` in
`consumer.sources`.
```js
// ...
if (consumer.hasContentsOfAllSources()) {
consumerReadyCallback(consumer);
} else {
fetchSources(consumer, consumerReadyCallback);
}
// ...
```
#### SourceMapConsumer.prototype.sourceContentFor(source[, returnNullOnMissing])
Returns the original source content for the source provided. The only
argument is the URL of the original source file.
If the source content for the given source is not found, then an error is
thrown. Optionally, pass `true` as the second param to have `null` returned
instead.
```js
consumer.sources
// [ "my-cool-lib.clj" ]
consumer.sourceContentFor("my-cool-lib.clj")
// "..."
consumer.sourceContentFor("this is not in the source map");
// Error: "this is not in the source map" is not in the source map
consumer.sourceContentFor("this is not in the source map", true);
// null
```
#### SourceMapConsumer.prototype.eachMapping(callback, context, order)
Iterate over each mapping between an original source/line/column and a
generated line/column in this source map.
* `callback`: The function that is called with each mapping. Mappings have the
form `{ source, generatedLine, generatedColumn, originalLine, originalColumn,
name }`
* `context`: Optional. If specified, this object will be the value of `this`
every time that `callback` is called.
* `order`: Either `SourceMapConsumer.GENERATED_ORDER` or
`SourceMapConsumer.ORIGINAL_ORDER`. Specifies whether you want to iterate over
the mappings sorted by the generated file's line/column order or the
original's source/line/column order, respectively. Defaults to
`SourceMapConsumer.GENERATED_ORDER`.
```js
consumer.eachMapping(function (m) { console.log(m); })
// ...
// { source: 'illmatic.js',
// generatedLine: 1,
// generatedColumn: 0,
// originalLine: 1,
// originalColumn: 0,
// name: null }
// { source: 'illmatic.js',
// generatedLine: 2,
// generatedColumn: 0,
// originalLine: 2,
// originalColumn: 0,
// name: null }
// ...
```
### SourceMapGenerator
An instance of the SourceMapGenerator represents a source map which is being
built incrementally.
#### new SourceMapGenerator([startOfSourceMap])
You may pass an object with the following properties:
* `file`: The filename of the generated source that this source map is
associated with.
* `sourceRoot`: A root for all relative URLs in this source map.
* `skipValidation`: Optional. When `true`, disables validation of mappings as
they are added. This can improve performance but should be used with
discretion, as a last resort. Even then, one should avoid using this flag when
running tests, if possible.
```js
var generator = new sourceMap.SourceMapGenerator({
file: "my-generated-javascript-file.js",
sourceRoot: "http://example.com/app/js/"
});
```
#### SourceMapGenerator.fromSourceMap(sourceMapConsumer)
Creates a new `SourceMapGenerator` from an existing `SourceMapConsumer` instance.
* `sourceMapConsumer` The SourceMap.
```js
var generator = sourceMap.SourceMapGenerator.fromSourceMap(consumer);
```
#### SourceMapGenerator.prototype.addMapping(mapping)
Add a single mapping from original source line and column to the generated
source's line and column for this source map being created. The mapping object
should have the following properties:
* `generated`: An object with the generated line and column positions.
* `original`: An object with the original line and column positions.
* `source`: The original source file (relative to the sourceRoot).
* `name`: An optional original token name for this mapping.
```js
generator.addMapping({
source: "module-one.scm",
original: { line: 128, column: 0 },
generated: { line: 3, column: 456 }
})
```
#### SourceMapGenerator.prototype.setSourceContent(sourceFile, sourceContent)
Set the source content for an original source file.
* `sourceFile` the URL of the original source file.
* `sourceContent` the content of the source file.
```js
generator.setSourceContent("module-one.scm",
fs.readFileSync("path/to/module-one.scm"))
```
#### SourceMapGenerator.prototype.applySourceMap(sourceMapConsumer[, sourceFile[, sourceMapPath]])
Applies a SourceMap for a source file to the SourceMap.
Each mapping to the supplied source file is rewritten using the
supplied SourceMap. Note: The resolution for the resulting mappings
is the minimum of this map and the supplied map.
* `sourceMapConsumer`: The SourceMap to be applied.
* `sourceFile`: Optional. The filename of the source file.
If omitted, sourceMapConsumer.file will be used, if it exists.
Otherwise an error will be thrown.
* `sourceMapPath`: Optional. The dirname of the path to the SourceMap
to be applied. If relative, it is relative to the SourceMap.
This parameter is needed when the two SourceMaps aren't in the same
directory, and the SourceMap to be applied contains relative source
paths. If so, those relative source paths need to be rewritten
relative to the SourceMap.
If omitted, it is assumed that both SourceMaps are in the same directory,
thus not needing any rewriting. (Supplying `'.'` has the same effect.)
#### SourceMapGenerator.prototype.toString()
Renders the source map being generated to a string.
```js
generator.toString()
// '{"version":3,"sources":["module-one.scm"],"names":[],"mappings":"...snip...","file":"my-generated-javascript-file.js","sourceRoot":"http://example.com/app/js/"}'
```
### SourceNode
SourceNodes provide a way to abstract over interpolating and/or concatenating
snippets of generated JavaScript source code, while maintaining the line and
column information associated between those snippets and the original source
code. This is useful as the final intermediate representation a compiler might
use before outputting the generated JS and source map.
#### new SourceNode([line, column, source[, chunk[, name]]])
* `line`: The original line number associated with this source node, or null if
it isn't associated with an original line. The line number is 1-based.
* `column`: The original column number associated with this source node, or null
if it isn't associated with an original column. The column number
is 0-based.
* `source`: The original source's filename; null if no filename is provided.
* `chunk`: Optional. Is immediately passed to `SourceNode.prototype.add`, see
below.
* `name`: Optional. The original identifier.
```js
var node = new SourceNode(1, 2, "a.cpp", [
new SourceNode(3, 4, "b.cpp", "extern int status;\n"),
new SourceNode(5, 6, "c.cpp", "std::string* make_string(size_t n);\n"),
new SourceNode(7, 8, "d.cpp", "int main(int argc, char** argv) {}\n"),
]);
```
#### SourceNode.fromStringWithSourceMap(code, sourceMapConsumer[, relativePath])
Creates a SourceNode from generated code and a SourceMapConsumer.
* `code`: The generated code
* `sourceMapConsumer` The SourceMap for the generated code
* `relativePath` The optional path that relative sources in `sourceMapConsumer`
should be relative to.
```js
const consumer = await new SourceMapConsumer(fs.readFileSync("path/to/my-file.js.map", "utf8"));
const node = SourceNode.fromStringWithSourceMap(fs.readFileSync("path/to/my-file.js"), consumer);
```
#### SourceNode.prototype.add(chunk)
Add a chunk of generated JS to this source node.
* `chunk`: A string snippet of generated JS code, another instance of
`SourceNode`, or an array where each member is one of those things.
```js
node.add(" + ");
node.add(otherNode);
node.add([leftHandOperandNode, " + ", rightHandOperandNode]);
```
#### SourceNode.prototype.prepend(chunk)
Prepend a chunk of generated JS to this source node.
* `chunk`: A string snippet of generated JS code, another instance of
`SourceNode`, or an array where each member is one of those things.
```js
node.prepend("/** Build Id: f783haef86324gf **/\n\n");
```
#### SourceNode.prototype.setSourceContent(sourceFile, sourceContent)
Set the source content for a source file. This will be added to the
`SourceMap` in the `sourcesContent` field.
* `sourceFile`: The filename of the source file
* `sourceContent`: The content of the source file
```js
node.setSourceContent("module-one.scm",
fs.readFileSync("path/to/module-one.scm"))
```
#### SourceNode.prototype.walk(fn)
Walk over the tree of JS snippets in this node and its children. The walking
function is called once for each snippet of JS and is passed that snippet and
the its original associated source's line/column location.
* `fn`: The traversal function.
```js
var node = new SourceNode(1, 2, "a.js", [
new SourceNode(3, 4, "b.js", "uno"),
"dos",
[
"tres",
new SourceNode(5, 6, "c.js", "quatro")
]
]);
node.walk(function (code, loc) { console.log("WALK:", code, loc); })
// WALK: uno { source: 'b.js', line: 3, column: 4, name: null }
// WALK: dos { source: 'a.js', line: 1, column: 2, name: null }
// WALK: tres { source: 'a.js', line: 1, column: 2, name: null }
// WALK: quatro { source: 'c.js', line: 5, column: 6, name: null }
```
#### SourceNode.prototype.walkSourceContents(fn)
Walk over the tree of SourceNodes. The walking function is called for each
source file content and is passed the filename and source content.
* `fn`: The traversal function.
```js
var a = new SourceNode(1, 2, "a.js", "generated from a");
a.setSourceContent("a.js", "original a");
var b = new SourceNode(1, 2, "b.js", "generated from b");
b.setSourceContent("b.js", "original b");
var c = new SourceNode(1, 2, "c.js", "generated from c");
c.setSourceContent("c.js", "original c");
var node = new SourceNode(null, null, null, [a, b, c]);
node.walkSourceContents(function (source, contents) { console.log("WALK:", source, ":", contents); })
// WALK: a.js : original a
// WALK: b.js : original b
// WALK: c.js : original c
```
#### SourceNode.prototype.join(sep)
Like `Array.prototype.join` except for SourceNodes. Inserts the separator
between each of this source node's children.
* `sep`: The separator.
```js
var lhs = new SourceNode(1, 2, "a.rs", "my_copy");
var operand = new SourceNode(3, 4, "a.rs", "=");
var rhs = new SourceNode(5, 6, "a.rs", "orig.clone()");
var node = new SourceNode(null, null, null, [ lhs, operand, rhs ]);
var joinedNode = node.join(" ");
```
#### SourceNode.prototype.replaceRight(pattern, replacement)
Call `String.prototype.replace` on the very right-most source snippet. Useful
for trimming white space from the end of a source node, etc.
* `pattern`: The pattern to replace.
* `replacement`: The thing to replace the pattern with.
```js
// Trim trailing white space.
node.replaceRight(/\s*$/, "");
```
#### SourceNode.prototype.toString()
Return the string representation of this source node. Walks over the tree and
concatenates all the various snippets together to one string.
```js
var node = new SourceNode(1, 2, "a.js", [
new SourceNode(3, 4, "b.js", "uno"),
"dos",
[
"tres",
new SourceNode(5, 6, "c.js", "quatro")
]
]);
node.toString()
// 'unodostresquatro'
```
#### SourceNode.prototype.toStringWithSourceMap([startOfSourceMap])
Returns the string representation of this tree of source nodes, plus a
SourceMapGenerator which contains all the mappings between the generated and
original sources.
The arguments are the same as those to `new SourceMapGenerator`.
```js
var node = new SourceNode(1, 2, "a.js", [
new SourceNode(3, 4, "b.js", "uno"),
"dos",
[
"tres",
new SourceNode(5, 6, "c.js", "quatro")
]
]);
node.toStringWithSourceMap({ file: "my-output-file.js" })
// { code: 'unodostresquatro',
// map: [object SourceMapGenerator] }
```
File diff suppressed because it is too large Load Diff
+100
View File
@@ -0,0 +1,100 @@
/* -*- Mode: js; js-indent-level: 2; -*- */
/*
* Copyright 2011 Mozilla Foundation and contributors
* Licensed under the New BSD license. See LICENSE or:
* http://opensource.org/licenses/BSD-3-Clause
*/
/**
* A data structure which is a combination of an array and a set. Adding a new
* member is O(1), testing for membership is O(1), and finding the index of an
* element is O(1). Removing elements from the set is not supported. Only
* strings are supported for membership.
*/
class ArraySet {
constructor() {
this._array = [];
this._set = new Map();
}
/**
* Static method for creating ArraySet instances from an existing array.
*/
static fromArray(aArray, aAllowDuplicates) {
const set = new ArraySet();
for (let i = 0, len = aArray.length; i < len; i++) {
set.add(aArray[i], aAllowDuplicates);
}
return set;
}
/**
* Return how many unique items are in this ArraySet. If duplicates have been
* added, than those do not count towards the size.
*
* @returns Number
*/
size() {
return this._set.size;
}
/**
* Add the given string to this set.
*
* @param String aStr
*/
add(aStr, aAllowDuplicates) {
const isDuplicate = this.has(aStr);
const idx = this._array.length;
if (!isDuplicate || aAllowDuplicates) {
this._array.push(aStr);
}
if (!isDuplicate) {
this._set.set(aStr, idx);
}
}
/**
* Is the given string a member of this set?
*
* @param String aStr
*/
has(aStr) {
return this._set.has(aStr);
}
/**
* What is the index of the given string in the array?
*
* @param String aStr
*/
indexOf(aStr) {
const idx = this._set.get(aStr);
if (idx >= 0) {
return idx;
}
throw new Error('"' + aStr + '" is not in the set.');
}
/**
* What is the element at the given index?
*
* @param Number aIdx
*/
at(aIdx) {
if (aIdx >= 0 && aIdx < this._array.length) {
return this._array[aIdx];
}
throw new Error("No element indexed by " + aIdx);
}
/**
* Returns the array representation of this set (which has the proper indices
* indicated by indexOf). Note that this is a copy of the internal array used
* for storing the members so that no one can mess with internal state.
*/
toArray() {
return this._array.slice();
}
}
exports.ArraySet = ArraySet;
+111
View File
@@ -0,0 +1,111 @@
/* -*- Mode: js; js-indent-level: 2; -*- */
/*
* Copyright 2011 Mozilla Foundation and contributors
* Licensed under the New BSD license. See LICENSE or:
* http://opensource.org/licenses/BSD-3-Clause
*
* Based on the Base 64 VLQ implementation in Closure Compiler:
* https://code.google.com/p/closure-compiler/source/browse/trunk/src/com/google/debugging/sourcemap/Base64VLQ.java
*
* Copyright 2011 The Closure Compiler Authors. All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of Google Inc. nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
const base64 = require("./base64");
// A single base 64 digit can contain 6 bits of data. For the base 64 variable
// length quantities we use in the source map spec, the first bit is the sign,
// the next four bits are the actual value, and the 6th bit is the
// continuation bit. The continuation bit tells us whether there are more
// digits in this value following this digit.
//
// Continuation
// | Sign
// | |
// V V
// 101011
const VLQ_BASE_SHIFT = 5;
// binary: 100000
const VLQ_BASE = 1 << VLQ_BASE_SHIFT;
// binary: 011111
const VLQ_BASE_MASK = VLQ_BASE - 1;
// binary: 100000
const VLQ_CONTINUATION_BIT = VLQ_BASE;
/**
* Converts from a two-complement value to a value where the sign bit is
* placed in the least significant bit. For example, as decimals:
* 1 becomes 2 (10 binary), -1 becomes 3 (11 binary)
* 2 becomes 4 (100 binary), -2 becomes 5 (101 binary)
*/
function toVLQSigned(aValue) {
return aValue < 0
? ((-aValue) << 1) + 1
: (aValue << 1) + 0;
}
/**
* Converts to a two-complement value from a value where the sign bit is
* placed in the least significant bit. For example, as decimals:
* 2 (10 binary) becomes 1, 3 (11 binary) becomes -1
* 4 (100 binary) becomes 2, 5 (101 binary) becomes -2
*/
// eslint-disable-next-line no-unused-vars
function fromVLQSigned(aValue) {
const isNegative = (aValue & 1) === 1;
const shifted = aValue >> 1;
return isNegative
? -shifted
: shifted;
}
/**
* Returns the base 64 VLQ encoded value.
*/
exports.encode = function base64VLQ_encode(aValue) {
let encoded = "";
let digit;
let vlq = toVLQSigned(aValue);
do {
digit = vlq & VLQ_BASE_MASK;
vlq >>>= VLQ_BASE_SHIFT;
if (vlq > 0) {
// There are still more digits in this value, so we must make sure the
// continuation bit is marked.
digit |= VLQ_CONTINUATION_BIT;
}
encoded += base64.encode(digit);
} while (vlq > 0);
return encoded;
};
+18
View File
@@ -0,0 +1,18 @@
/* -*- Mode: js; js-indent-level: 2; -*- */
/*
* Copyright 2011 Mozilla Foundation and contributors
* Licensed under the New BSD license. See LICENSE or:
* http://opensource.org/licenses/BSD-3-Clause
*/
const intToCharMap = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".split("");
/**
* Encode an integer in the range of 0 to 63 to a single base 64 digit.
*/
exports.encode = function(number) {
if (0 <= number && number < intToCharMap.length) {
return intToCharMap[number];
}
throw new TypeError("Must be between 0 and 63: " + number);
};
+107
View File
@@ -0,0 +1,107 @@
/* -*- Mode: js; js-indent-level: 2; -*- */
/*
* Copyright 2011 Mozilla Foundation and contributors
* Licensed under the New BSD license. See LICENSE or:
* http://opensource.org/licenses/BSD-3-Clause
*/
exports.GREATEST_LOWER_BOUND = 1;
exports.LEAST_UPPER_BOUND = 2;
/**
* Recursive implementation of binary search.
*
* @param aLow Indices here and lower do not contain the needle.
* @param aHigh Indices here and higher do not contain the needle.
* @param aNeedle The element being searched for.
* @param aHaystack The non-empty array being searched.
* @param aCompare Function which takes two elements and returns -1, 0, or 1.
* @param aBias Either 'binarySearch.GREATEST_LOWER_BOUND' or
* 'binarySearch.LEAST_UPPER_BOUND'. Specifies whether to return the
* closest element that is smaller than or greater than the one we are
* searching for, respectively, if the exact element cannot be found.
*/
function recursiveSearch(aLow, aHigh, aNeedle, aHaystack, aCompare, aBias) {
// This function terminates when one of the following is true:
//
// 1. We find the exact element we are looking for.
//
// 2. We did not find the exact element, but we can return the index of
// the next-closest element.
//
// 3. We did not find the exact element, and there is no next-closest
// element than the one we are searching for, so we return -1.
const mid = Math.floor((aHigh - aLow) / 2) + aLow;
const cmp = aCompare(aNeedle, aHaystack[mid], true);
if (cmp === 0) {
// Found the element we are looking for.
return mid;
} else if (cmp > 0) {
// Our needle is greater than aHaystack[mid].
if (aHigh - mid > 1) {
// The element is in the upper half.
return recursiveSearch(mid, aHigh, aNeedle, aHaystack, aCompare, aBias);
}
// The exact needle element was not found in this haystack. Determine if
// we are in termination case (3) or (2) and return the appropriate thing.
if (aBias == exports.LEAST_UPPER_BOUND) {
return aHigh < aHaystack.length ? aHigh : -1;
}
return mid;
}
// Our needle is less than aHaystack[mid].
if (mid - aLow > 1) {
// The element is in the lower half.
return recursiveSearch(aLow, mid, aNeedle, aHaystack, aCompare, aBias);
}
// we are in termination case (3) or (2) and return the appropriate thing.
if (aBias == exports.LEAST_UPPER_BOUND) {
return mid;
}
return aLow < 0 ? -1 : aLow;
}
/**
* This is an implementation of binary search which will always try and return
* the index of the closest element if there is no exact hit. This is because
* mappings between original and generated line/col pairs are single points,
* and there is an implicit region between each of them, so a miss just means
* that you aren't on the very start of a region.
*
* @param aNeedle The element you are looking for.
* @param aHaystack The array that is being searched.
* @param aCompare A function which takes the needle and an element in the
* array and returns -1, 0, or 1 depending on whether the needle is less
* than, equal to, or greater than the element, respectively.
* @param aBias Either 'binarySearch.GREATEST_LOWER_BOUND' or
* 'binarySearch.LEAST_UPPER_BOUND'. Specifies whether to return the
* closest element that is smaller than or greater than the one we are
* searching for, respectively, if the exact element cannot be found.
* Defaults to 'binarySearch.GREATEST_LOWER_BOUND'.
*/
exports.search = function search(aNeedle, aHaystack, aCompare, aBias) {
if (aHaystack.length === 0) {
return -1;
}
let index = recursiveSearch(-1, aHaystack.length, aNeedle, aHaystack,
aCompare, aBias || exports.GREATEST_LOWER_BOUND);
if (index < 0) {
return -1;
}
// We have found either the exact element, or the next-closest element than
// the one we are searching for. However, there may be more than one such
// element. Make sure we always return the smallest of these.
while (index - 1 >= 0) {
if (aCompare(aHaystack[index], aHaystack[index - 1], true) !== 0) {
break;
}
--index;
}
return index;
};
+80
View File
@@ -0,0 +1,80 @@
/* -*- Mode: js; js-indent-level: 2; -*- */
/*
* Copyright 2014 Mozilla Foundation and contributors
* Licensed under the New BSD license. See LICENSE or:
* http://opensource.org/licenses/BSD-3-Clause
*/
const util = require("./util");
/**
* Determine whether mappingB is after mappingA with respect to generated
* position.
*/
function generatedPositionAfter(mappingA, mappingB) {
// Optimized for most common case
const lineA = mappingA.generatedLine;
const lineB = mappingB.generatedLine;
const columnA = mappingA.generatedColumn;
const columnB = mappingB.generatedColumn;
return lineB > lineA || lineB == lineA && columnB >= columnA ||
util.compareByGeneratedPositionsInflated(mappingA, mappingB) <= 0;
}
/**
* A data structure to provide a sorted view of accumulated mappings in a
* performance conscious manner. It trades a negligible overhead in general
* case for a large speedup in case of mappings being added in order.
*/
class MappingList {
constructor() {
this._array = [];
this._sorted = true;
// Serves as infimum
this._last = {generatedLine: -1, generatedColumn: 0};
}
/**
* Iterate through internal items. This method takes the same arguments that
* `Array.prototype.forEach` takes.
*
* NOTE: The order of the mappings is NOT guaranteed.
*/
unsortedForEach(aCallback, aThisArg) {
this._array.forEach(aCallback, aThisArg);
}
/**
* Add the given source mapping.
*
* @param Object aMapping
*/
add(aMapping) {
if (generatedPositionAfter(this._last, aMapping)) {
this._last = aMapping;
this._array.push(aMapping);
} else {
this._sorted = false;
this._array.push(aMapping);
}
}
/**
* Returns the flat, sorted array of mappings. The mappings are sorted by
* generated position.
*
* WARNING: This method returns internal data without copying, for
* performance. The return value must NOT be mutated, and should be treated as
* an immutable borrow. If you want to take ownership, you must make your own
* copy.
*/
toArray() {
if (!this._sorted) {
this._array.sort(util.compareByGeneratedPositionsInflated);
this._sorted = true;
}
return this._array;
}
}
exports.MappingList = MappingList;
Binary file not shown.
+40
View File
@@ -0,0 +1,40 @@
if (typeof fetch === "function") {
// Web version of reading a wasm file into an array buffer.
let mappingsWasmUrl = null;
module.exports = function readWasm() {
if (typeof mappingsWasmUrl !== "string") {
throw new Error("You must provide the URL of lib/mappings.wasm by calling " +
"SourceMapConsumer.initialize({ 'lib/mappings.wasm': ... }) " +
"before using SourceMapConsumer");
}
return fetch(mappingsWasmUrl)
.then(response => response.arrayBuffer());
};
module.exports.initialize = url => mappingsWasmUrl = url;
} else {
// Node version of reading a wasm file into an array buffer.
const fs = require("fs");
const path = require("path");
module.exports = function readWasm() {
return new Promise((resolve, reject) => {
const wasmPath = path.join(__dirname, "mappings.wasm");
fs.readFile(wasmPath, null, (error, data) => {
if (error) {
reject(error);
return;
}
resolve(data.buffer);
});
});
};
module.exports.initialize = _ => {
console.debug("SourceMapConsumer.initialize is a no-op when running in node.js");
};
}
File diff suppressed because it is too large Load Diff

Some files were not shown because too many files have changed in this diff Show More