initial commit
This commit is contained in:
190
node_modules/editions/source/index.js
generated
vendored
Normal file
190
node_modules/editions/source/index.js
generated
vendored
Normal file
@@ -0,0 +1,190 @@
|
||||
/* @flow */
|
||||
/* eslint no-console:0 */
|
||||
'use strict'
|
||||
|
||||
// Imports
|
||||
const pathUtil = require('path')
|
||||
|
||||
// Helper class to display nested error in a sensible way
|
||||
class DetailedError extends Error {
|
||||
constructor (message /* :string */, details /* :Object */) {
|
||||
Object.keys(details).forEach(function (key) {
|
||||
const data = details[key]
|
||||
const value = require('util').inspect(data.stack || data.message || data)
|
||||
message += `\n${key}: ${value}`
|
||||
})
|
||||
super(message)
|
||||
}
|
||||
}
|
||||
|
||||
// Environment fetching
|
||||
const blacklist = process && process.env && process.env.EDITIONS_SYNTAX_BLACKLIST && process.env.EDITIONS_SYNTAX_BLACKLIST.split(',')
|
||||
|
||||
// Cache of which syntax combinations are supported or unsupported, hash of booleans
|
||||
const syntaxFailedCombitions = {} // sorted lowercase syntax combination => Error instance of failure
|
||||
const syntaxBlacklist = {}
|
||||
syntaxBlacklist.import = new Error('The import syntax is skipped as the module package.json field eliminates the need for autoloader support')
|
||||
syntaxBlacklist.coffeescript = new Error('The coffeescript syntax is skipped as we want to use a precompiled edition rather than compiling at runtime')
|
||||
syntaxBlacklist.typescript = new Error('The typescript syntax is skipped as we want to use a precompiled edition rather than compiling at runtime')
|
||||
|
||||
// Blacklist non-esnext node versions from esnext
|
||||
if ( process && process.versions && process.versions.node ) {
|
||||
const EARLIEST_ESNEXT_NODE_VERSION = [0, 12]
|
||||
const NODE_VERSION = process.versions.node.split('.').map((n) => parseInt(n, 10))
|
||||
const ESNEXT_UNSUPPORTED = NODE_VERSION[0] < EARLIEST_ESNEXT_NODE_VERSION[0] || (
|
||||
NODE_VERSION[0] === EARLIEST_ESNEXT_NODE_VERSION[0] &&
|
||||
NODE_VERSION[1] < EARLIEST_ESNEXT_NODE_VERSION[1]
|
||||
)
|
||||
if ( ESNEXT_UNSUPPORTED ) syntaxBlacklist.esnext = new Error('The esnext syntax is skipped on early node versions as attempting to use esnext features will output debugging information on these node versions')
|
||||
}
|
||||
|
||||
// Check the environment configuration for a syntax blacklist
|
||||
if ( blacklist ) {
|
||||
for ( let i = 0; i < blacklist.length; ++i ) {
|
||||
const syntax = blacklist[i].trim().toLowerCase()
|
||||
syntaxBlacklist[syntax] = new DetailedError('The EDITIONS_SYNTAX_BLACKLIST environment variable has blacklisted an edition syntax:', {syntax, blacklist})
|
||||
}
|
||||
}
|
||||
|
||||
/* ::
|
||||
type edition = {
|
||||
name:number,
|
||||
description?:string,
|
||||
directory?:string,
|
||||
entry?:string,
|
||||
syntaxes?:Array<string>
|
||||
};
|
||||
type options = {
|
||||
cwd?:string,
|
||||
package?:string,
|
||||
entry?:string,
|
||||
require:function
|
||||
};
|
||||
*/
|
||||
|
||||
/**
|
||||
* Cycle through the editions and require the correct one
|
||||
* @protected internal function that is untested for public consumption
|
||||
* @param {edition} edition - the edition entry
|
||||
* @param {Object} opts - the following options
|
||||
* @param {string} opts.require - the require method of the calling module, used to ensure require paths remain correct
|
||||
* @param {string} [opts.cwd] - if provided, this will be the cwd for entries
|
||||
* @param {string} [opts.entry] - if provided, should be a relative or absolute path to the entry point of the edition
|
||||
* @param {string} [opts.package] - if provided, should be the name of the package that we are loading the editions for
|
||||
* @returns {*}
|
||||
*/
|
||||
function requireEdition ( edition /* :edition */, opts /* :options */ ) /* :any */ {
|
||||
// Prevent require from being included in debug logs
|
||||
Object.defineProperty(opts, 'require', {value: opts.require, enumerable: false})
|
||||
|
||||
// Get the correct entry path
|
||||
// As older versions o
|
||||
const cwd = opts.cwd || ''
|
||||
const dir = edition.directory || ''
|
||||
let entry = opts.entry || edition.entry || ''
|
||||
if ( dir && entry && entry.indexOf(dir + '/') === 0 ) entry = entry.substring(dir.length + 1)
|
||||
// ^ this should not be needed, but as previous versions of editions included the directory inside the entry
|
||||
// it unfortunately is, as such this is a stepping stone for the new format, the new format being
|
||||
// if entry is specified by itself, it is cwd => entry
|
||||
// if entry is specified with a directory, it is cwd => dir => entry
|
||||
// if entry is not specified but dir is, it is cwd => dir
|
||||
// if neither entry nor dir are specified, we have a problem
|
||||
if ( !dir && !entry ) {
|
||||
const editionFailure = new DetailedError('Skipped edition due to no entry or directory being specified:', {edition, cwd, dir, entry})
|
||||
throw editionFailure
|
||||
}
|
||||
const entryPath = pathUtil.resolve(cwd, dir, entry)
|
||||
|
||||
// Check syntax support
|
||||
// Convert syntaxes into a sorted lowercase string
|
||||
const syntaxes = edition.syntaxes && edition.syntaxes.map((i) => i.toLowerCase()).sort()
|
||||
const syntaxCombination = syntaxes && syntaxes.join(', ')
|
||||
if ( syntaxes && syntaxCombination ) {
|
||||
// Check if any of the syntaxes are unsupported
|
||||
const unsupportedSyntaxes = syntaxes.filter((i) => syntaxBlacklist[i.toLowerCase()])
|
||||
if ( unsupportedSyntaxes.length ) {
|
||||
const editionFailure = new DetailedError('Skipped edition due to it containing an unsupported syntax:', {edition, unsupportedSyntaxes})
|
||||
throw editionFailure
|
||||
}
|
||||
// Is this syntax combination unsupported? If so skip it with a soft failure to try the next edition
|
||||
else if ( syntaxFailedCombitions[syntaxCombination] ) {
|
||||
const previousCombinationFailure = syntaxFailedCombitions[syntaxCombination]
|
||||
const editionFailure = new DetailedError('Skipped edition due to its syntax combinatiom failing previously:', {edition, previousCombinationFailure})
|
||||
throw editionFailure
|
||||
}
|
||||
}
|
||||
|
||||
// Try and load this syntax combination
|
||||
try {
|
||||
return opts.require(entryPath)
|
||||
}
|
||||
catch ( error ) {
|
||||
// Note the error with more details
|
||||
const editionFailure = new DetailedError('Failed to load the edition due to a load error:', {edition, error: error.stack})
|
||||
|
||||
// Blacklist the combination, even if it may have worked before
|
||||
// Perhaps in the future note if that if it did work previously, then we should instruct module owners to be more specific with their syntaxes
|
||||
if ( syntaxCombination ) syntaxFailedCombitions[syntaxCombination] = editionFailure
|
||||
|
||||
// Continue to the next edition
|
||||
throw editionFailure
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Cycle through the editions and require the correct one
|
||||
* @protected internal function that is untested for public consumption
|
||||
* @param {Array<edition>} editions - an array of edition entries
|
||||
* @param {Object} opts - the following options
|
||||
* @param {string} opts.require - the require method of the calling module, used to ensure require paths remain correct
|
||||
* @param {string} [opts.cwd] - if provided, this will be the cwd for entries
|
||||
* @param {string} [opts.entry] - if provided, should be a relative path to the entry point of the edition
|
||||
* @param {string} [opts.package] - if provided, should be the name of the package that we are loading the editions for
|
||||
* @returns {*}
|
||||
*/
|
||||
function requireEditions ( editions /* :Array<edition> */, opts /* :options */ ) /* :any */ {
|
||||
// Extract
|
||||
if ( opts.package == null ) opts.package = 'custom runtime package'
|
||||
|
||||
// Check
|
||||
if ( !editions || editions.length === 0 ) {
|
||||
throw new DetailedError('No editions were specified:', {opts})
|
||||
}
|
||||
|
||||
// Note the last error message
|
||||
const editionFailures = []
|
||||
|
||||
// Cycle through the editions
|
||||
for ( let i = 0; i < editions.length; ++i ) {
|
||||
const edition = editions[i]
|
||||
try {
|
||||
return requireEdition(edition, opts)
|
||||
}
|
||||
catch ( err ) {
|
||||
editionFailures.push(err)
|
||||
}
|
||||
}
|
||||
|
||||
// Through the error as no edition loaded
|
||||
throw new DetailedError('There are no suitable editions for this environment:', {opts, editions, failures: editionFailures})
|
||||
}
|
||||
|
||||
/**
|
||||
* Cycle through the editions for a package and require the correct one
|
||||
* @param {string} cwd - the path of the package, used to load package.json:editions and handle relative edition entry points
|
||||
* @param {function} require - the require method of the calling module, used to ensure require paths remain correct
|
||||
* @param {string} [entry] - an optional override for the entry of an edition, requires the edition to specify a `directory` property
|
||||
* @returns {*}
|
||||
*/
|
||||
function requirePackage (cwd /* :string */, require /* :function */, entry /* :: ?:string */ ) /* :any */ {
|
||||
// Load the package.json file to fetch `name` for debugging and `editions` for loading
|
||||
const packagePath = pathUtil.resolve(cwd, 'package.json')
|
||||
const {name, editions} = require(packagePath)
|
||||
const opts /* :options */ = {cwd, require}
|
||||
if ( name ) opts.package = name
|
||||
if ( entry ) opts.entry = entry
|
||||
return requireEditions(editions, opts)
|
||||
}
|
||||
|
||||
// Exports
|
||||
module.exports = {requireEdition, requireEditions, requirePackage}
|
||||
Reference in New Issue
Block a user