@untool/core
Advanced tools
Comparing version 0.26.0 to 1.0.0-rc.0
25
index.js
'use strict'; | ||
const debug = require('debug')('untool:core'); | ||
const isPlainObject = require('is-plain-object'); | ||
const define = require('mixinable'); | ||
const { getConfig } = require('./lib/config'); | ||
const { environmentalize } = require('./lib/utils'); | ||
const { getConfig, getMixins } = require('./lib/config'); | ||
exports.Mixin = class Mixin { | ||
constructor(config, options) { | ||
constructor(config, ...args) { | ||
const options = args.slice(-1); | ||
this.config = config; | ||
this.options = options; | ||
this.options = isPlainObject(options) ? options : {}; | ||
} | ||
}; | ||
exports.bootstrap = function bootstrap(configOverrides = {}, options = {}) { | ||
const config = environmentalize(getConfig(configOverrides)); | ||
const mixins = config.mixins.core.map((mixin) => require(mixin)); | ||
exports.initialize = function initialize(overrides = {}, ...args) { | ||
const config = getConfig(overrides); | ||
const mixins = getMixins(config); | ||
const strategies = { | ||
@@ -26,11 +27,5 @@ ...mixins.reduce( | ||
debug(mixins.map(({ name, strategies }) => ({ [name]: strategies }))); | ||
return define(strategies, mixins)(config, options); | ||
return define(strategies, mixins)(config, ...args); | ||
}; | ||
exports.internal = { | ||
getConfig(...args) { | ||
return environmentalize(getConfig(...args)); | ||
}, | ||
environmentalize, | ||
}; | ||
exports.internal = { getConfig }; |
'use strict'; | ||
// This file is usually not being used at runtime, but only at buildtime. | ||
// @untool/webpack is taking care of providing runtime configuration. | ||
const { basename, dirname, join } = require('path'); | ||
const debug = require('debug')('untool:config'); | ||
const { sync: findUp } = require('find-up'); | ||
const { load: loadEnv } = require('dotenv'); | ||
const { createLoader } = require('./loader'); | ||
const { resolveMixins } = require('./resolve'); | ||
const { placeholdify, merge } = require('./utils'); | ||
const { createConfigLoader } = require('./loader'); | ||
const { createMixinResolver } = require('./resolver'); | ||
const { placeholdify, environmentalize } = require('./utils'); | ||
const defaultNamespace = process.env.UNTOOL_NSP || 'untool'; | ||
const defaultNamespace = 'untool'; | ||
const defaultMixinTypes = { | ||
@@ -24,21 +22,10 @@ core: ['core'], | ||
...overrides | ||
}) => { | ||
const pkgFile = findUp('package.json'); | ||
const pkgData = require(pkgFile); | ||
const rootDir = dirname(pkgFile); | ||
loadEnv({ path: join(rootDir, '.env') }); | ||
const { loadSettings, loadPresets } = createLoader(namespace); | ||
const { name = basename(rootDir), version = '0.0.0' } = pkgData; | ||
const defaults = { rootDir, name, version, mixins: [] }; | ||
const settings = loadSettings(rootDir, pkgData); | ||
const presets = loadPresets(rootDir, settings.presets); | ||
// eslint-disable-next-line no-unused-vars | ||
const { presets: _, ...raw } = merge(defaults, presets, settings, overrides); | ||
} = {}) => { | ||
const { loadConfig } = createConfigLoader(namespace); | ||
const { resolveMixins } = createMixinResolver(mixinTypes); | ||
const { presets, mixins, ...rawConfig } = loadConfig(overrides); | ||
const config = { | ||
...placeholdify(raw), | ||
mixins: resolveMixins(rootDir, raw.mixins, mixinTypes), | ||
...environmentalize(placeholdify(rawConfig)), | ||
_mixins: resolveMixins(rawConfig.rootDir, mixins), | ||
_presets: presets, | ||
}; | ||
@@ -48,1 +35,3 @@ debug(config); | ||
}; | ||
exports.getMixins = ({ _mixins: mixins }) => mixins.core.map(require); |
'use strict'; | ||
const { dirname, join } = require('path'); | ||
const { basename, dirname, join } = require('path'); | ||
const cosmiconfig = require('cosmiconfig'); | ||
const { resolve, resolvePreset, isResolveError } = require('./resolve'); | ||
const { load: loadEnv } = require('dotenv'); | ||
const { sync: findUp } = require('find-up'); | ||
const { merge } = require('./utils'); | ||
const { resolve, resolvePreset, isResolveError } = require('./resolver'); | ||
exports.createLoader = (namespace) => { | ||
const loadConfig = (context, config) => { | ||
const { loadSync, searchSync } = cosmiconfig(namespace, { | ||
stopDir: context, | ||
}); | ||
return config | ||
? loadSync(resolvePreset(context, config)) | ||
: searchSync(context); | ||
exports.createConfigLoader = (namespace) => { | ||
const loadConfig = (stopDir, module) => { | ||
const { loadSync } = cosmiconfig(namespace, { stopDir }); | ||
return loadSync(resolvePreset(stopDir, module)); | ||
}; | ||
const searchConfig = (stopDir) => { | ||
const { searchSync } = cosmiconfig(namespace, { stopDir }); | ||
return searchSync(stopDir); | ||
}; | ||
const loadPreset = (context, preset) => { | ||
@@ -25,3 +29,5 @@ try { | ||
try { | ||
return loadConfig(dirname(resolve(context, `${preset}/package.json`))); | ||
return searchConfig( | ||
dirname(resolve(context, `${preset}/package.json`)) | ||
); | ||
} catch (error) { | ||
@@ -33,40 +39,53 @@ if (!isResolveError(error)) throw error; | ||
}; | ||
return { | ||
loadSettings: function loadSettings(context, pkgData) { | ||
const { dependencies = {}, devDependencies = {} } = pkgData; | ||
const result = loadConfig(context); | ||
const settings = { | ||
...(result ? result.config : {}), | ||
}; | ||
if (!settings.presets) { | ||
settings.presets = Object.keys(dependencies) | ||
.concat( | ||
process.env.NODE_ENV !== 'production' | ||
? Object.keys(devDependencies) | ||
: [] | ||
) | ||
.filter((key) => { | ||
try { | ||
return loadConfig(context, key); | ||
} catch (error) { | ||
if (!isResolveError(error)) throw error; | ||
return null; | ||
} | ||
}); | ||
const loadPresets = (context, presets = []) => | ||
presets.reduce((configs, preset) => { | ||
const { config, filepath } = loadPreset(context, preset); | ||
const newContext = dirname(filepath); | ||
if (config.mixins) { | ||
config.mixins = config.mixins.map( | ||
(mixin) => (mixin.startsWith('.') ? join(newContext, mixin) : mixin) | ||
); | ||
} | ||
return settings; | ||
}, | ||
loadPresets: function loadPresets(context, presets = []) { | ||
return presets.reduce((configs, preset) => { | ||
const { config, filepath } = loadPreset(context, preset); | ||
const newContext = dirname(filepath); | ||
if (config.mixins) { | ||
config.mixins = config.mixins.map( | ||
(mixin) => (mixin.startsWith('.') ? join(newContext, mixin) : mixin) | ||
); | ||
return merge(configs, loadPresets(newContext, config.presets), config); | ||
}, {}); | ||
const loadSettings = (context, pkgData) => { | ||
const { dependencies = {}, devDependencies = {} } = pkgData; | ||
const result = searchConfig(context); | ||
const settings = { ...(result && result.config) }; | ||
if (!settings.presets) { | ||
settings.presets = [ | ||
...Object.keys(dependencies), | ||
...(process.env.NODE_ENV !== 'production' | ||
? Object.keys(devDependencies) | ||
: []), | ||
].filter((key) => { | ||
try { | ||
return loadConfig(context, key); | ||
} catch (error) { | ||
if (!isResolveError(error)) throw error; | ||
return null; | ||
} | ||
return merge(configs, loadPresets(newContext, config.presets), config); | ||
}, {}); | ||
}); | ||
} | ||
return settings; | ||
}; | ||
return { | ||
loadConfig: function loadConfig(overrides) { | ||
const pkgFile = findUp('package.json'); | ||
const pkgData = require(pkgFile); | ||
const rootDir = dirname(pkgFile); | ||
const { name = basename(rootDir), version = '0.0.0' } = pkgData; | ||
loadEnv({ path: join(rootDir, '.env') }); | ||
const defaults = { rootDir, name, version, mixins: [] }; | ||
const settings = loadSettings(rootDir, pkgData); | ||
const presets = loadPresets(rootDir, settings.presets); | ||
return merge(defaults, presets, settings, overrides); | ||
}, | ||
}; | ||
}; |
@@ -12,3 +12,3 @@ 'use strict'; | ||
if ('mixins' === key) { | ||
return objValue.concat(srcValue); | ||
return [...objValue, ...srcValue]; | ||
} | ||
@@ -21,4 +21,4 @@ return srcValue; | ||
const flatConfig = flatten(config); | ||
const flatKeys = Object.keys(flatConfig); | ||
const regExp = new RegExp(`<(${flatKeys.map(escapeRegExp).join('|')})>`, 'g'); | ||
const configPaths = Object.keys(flatConfig).map(escapeRegExp); | ||
const regExp = new RegExp(`<(${configPaths.join('|')})>`, 'g'); | ||
const replaceRecursive = (item) => { | ||
@@ -29,6 +29,9 @@ if (Array.isArray(item)) { | ||
if (isPlainObject(item)) { | ||
return Object.keys(item).reduce((result, key) => { | ||
result[key] = replaceRecursive(item[key]); | ||
return result; | ||
}, {}); | ||
return Object.entries(item).reduce( | ||
(result, [key, value]) => ({ | ||
...result, | ||
[key]: replaceRecursive(value), | ||
}), | ||
{} | ||
); | ||
} | ||
@@ -54,6 +57,9 @@ if (regExp.test(item)) { | ||
if (isPlainObject(item)) { | ||
return Object.keys(item).reduce((result, key) => { | ||
result[key] = replaceRecursive(item[key]); | ||
return result; | ||
}, {}); | ||
return Object.entries(item).reduce( | ||
(result, [key, value]) => ({ | ||
...result, | ||
[key]: replaceRecursive(value), | ||
}), | ||
{} | ||
); | ||
} | ||
@@ -67,3 +73,3 @@ if (regExp.test(item)) { | ||
}; | ||
return Object.assign(replaceRecursive(_config), { _config, _env }); | ||
return { ...replaceRecursive(_config), _config, _env }; | ||
}; |
{ | ||
"name": "@untool/core", | ||
"version": "0.26.0", | ||
"version": "1.0.0-rc.0", | ||
"description": "untool core", | ||
@@ -38,3 +38,3 @@ "jsnext": "index.js", | ||
}, | ||
"gitHead": "0a63533a040db7b2bf27447320541510682b740f" | ||
"gitHead": "87b917a766013bab1caf2d312f3428e5dd329c12" | ||
} |
@@ -170,24 +170,6 @@ # `@untool/core` | ||
### `render([...args])` (runtime only) | ||
### `initialize([configOverrides], [options])` | ||
This function only exists if [`@untool/webpack`](https://github.com/untool/untool/blob/master/packages/webpack/README.md) is installed and active. You are expected to call it in your applications main entry file and it is essentialy a shorthand: it creates and bootstraps a core mixin container and calls its `render` method. | ||
Whatever arguments it receives are being passed along to its container's mixins' constructors. For it to work, you need to register at least one mixin implementing the `render` method. The default render mixin is [`@untool/react`](https://github.com/untool/untool/blob/master/packages/react/README.md). | ||
Render mixins are expected to return functions from their render implementations: an [Express middleware](https://expressjs.com/en/guide/using-middleware.html) on the server or a function that bootstraps and starts a client side app in the browser. | ||
```javascript | ||
import React from 'react'; | ||
import { render } from '@untool/core'; | ||
export default render(<h1>hello world</h1>); | ||
``` | ||
The render function serves two main purposes: 'universalifying' or 'isomorphizing' you application, i.e. making sure your app's code can run both on a server and in a browser, and integrating `untool`'s build and runtime environments. | ||
`Mixin` aside, `render` probably is the only part of `untool` you will directly interact with in your own code. It certainly is the only one of its APIs you will ever use within your application. | ||
### `bootstrap([configOverrides], [options])` | ||
This is a semi-private function that is mainly being used internally, for example by [`@untool/yargs`](https://github.com/untool/untool/blob/master/packages/yargs/README.md). It returns the core mixin container - this allows you to call all defined mixin methods. | ||
You will only ever have to call it if you want to use `@untool/core` programmatically. You can pass it an `configOverrides` object that will be merged into the main config object, and and options object mixins might use instead of CLI arguments. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
243
3
24438
175