Socket
Socket
Sign inDemoInstall

@shopify/slate-config

Package Overview
Dependencies
Maintainers
4
Versions
29
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@shopify/slate-config - npm Package Compare versions

Comparing version 1.0.0-beta.7 to 1.0.0-beta.8

__tests__/fixtures/slateWithError.config.js

8

__tests__/fixtures/slate.config.js
module.exports = {
testSchema: {
'test-item': 'override-value',
},
otherSchema: {
'other-item': 'other-value',
},
'some.key': 'override-value',
'other.item': 'other-value',
};

@@ -1,45 +0,145 @@

const slateConfig = require('../index');
const testSchema = require('./fixtures/testSchema');
const otherSchema = require('./fixtures/otherSchema');
const path = require('path');
describe('.generate()', () => {
describe('can generate a config object', () => {
test('with default values', () => {
const config = slateConfig.generate(testSchema);
const originalCwd = process.cwd();
const schema = {
'some.key': 'someValue',
'some.other.key': 'someOtherValue',
'some.function': jest.fn((config) => config.get('some.other.key')),
};
expect(config).toHaveProperty(
testSchema.items[0].id,
testSchema.items[0].default,
);
});
beforeEach(() => {
process.chdir(originalCwd);
global.slateUserConfig = null;
global.slateConfigPath = null;
jest.resetModules();
});
test('with slaterc overrides', () => {
const mockSlateRc = require('./fixtures/slate.config.js');
const config = slateConfig.generate(testSchema, mockSlateRc);
describe('when the file first is executed it', () => {
test('looks for slate.config.js in process.cwd and assigns its contents to a global variable global.slateUserConfig', () => {
process.chdir(path.resolve(__dirname, './fixtures'));
expect(config).toHaveProperty(testSchema.items[0].id, 'override-value');
});
const userConfig = require('./fixtures/slate.config');
test('with namespaced settings', () => {
const mockSlateRc = require('./fixtures/slate.config.js');
const config1 = slateConfig.generate(testSchema, mockSlateRc);
const config2 = slateConfig.generate(otherSchema, mockSlateRc);
require('../index');
expect(config1).toHaveProperty(
testSchema.items[0].id,
mockSlateRc[testSchema.id][testSchema.items[0].id],
);
expect(config2).toHaveProperty(
otherSchema.items[0].id,
mockSlateRc[otherSchema.id][otherSchema.items[0].id],
);
});
expect(global.slateUserConfig).toBeDefined();
expect(global.slateUserConfig).toMatchObject(userConfig);
});
test('with the schema used to generate the config', () => {
const config = slateConfig.generate(testSchema);
test('looks for a slate.config.js file if global.slateConfigPath is defined', () => {
global.slateConfigPath = path.resolve(
__dirname,
'fixtures/slate.config.js',
);
expect(config).toHaveProperty('__schema');
expect(config.__schema).toEqual(testSchema);
});
const userConfig = require('./fixtures/slate.config');
require('../index');
expect(global.slateUserConfig).toBeDefined();
expect(global.slateUserConfig).toMatchObject(userConfig);
});
test('if slate.config.js does not exist at process.cwd or the value specified by global.slateConfigPath, an empty object is returned', () => {
require('../index');
expect(global.slateUserConfig).toBeDefined();
expect(global.slateUserConfig).toMatchObject({});
});
test('throws error if there is an error in the slate.config.js file', () => {
global.slateConfigPath = path.resolve(
__dirname,
'fixtures/slateWithError.config.js',
);
expect(() => {
require('../index');
}).toThrowError(ReferenceError);
});
});
describe('SlateConfig()', () => {
test('requires a first argument which is assigned to this.schema', () => {
const SlateConfig = require('../index');
const config = new SlateConfig(schema);
expect(config.schema).toMatchObject(schema);
expect(() => new SlateConfig()).toThrowError();
});
test('SlateConfig.prototype.userConfig is a shortcut to global.slateUserConfig', () => {
process.chdir(path.resolve(__dirname, './fixtures'));
const SlateConfig = require('../index');
const config = new SlateConfig(schema);
expect(config.userConfig).toMatchObject(global.slateUserConfig);
});
test('does not modify the original schema object', () => {
const SlateConfig = require('../index');
const config = new SlateConfig(schema);
config.get('some.function');
expect(config.schema).not.toBe(schema);
});
});
describe('SlateConfig.get()', () => {
test('fetches the value of the provided key', () => {
const SlateConfig = require('../index');
const config = new SlateConfig(schema);
expect(config.get('some.other.key')).toBe(schema['some.other.key']);
});
test('if the value is a function and not specified in this.userConfig, the function is executed with the config instance as the only argument', () => {
const SlateConfig = require('../index');
const config = new SlateConfig(schema);
const value = config.get('some.function');
expect(schema['some.function']).toBeCalledWith(config);
expect(value).toBe(schema['some.other.key']);
});
test('if the value is specified in this.userConfig and is a function, it is executed with the config instance and the computed default value as arguments', () => {
const userConfigValue = 'someNewValue';
const userConfigFunction = jest.fn(() => userConfigValue);
const SlateConfig = require('../index');
const config = new SlateConfig(schema);
const defaultValue = config.get('some.function');
global.slateUserConfig = {
'some.function': userConfigFunction,
};
const value = config.get('some.function');
expect(global.slateUserConfig['some.function']).toBeCalledWith(
config,
defaultValue,
);
expect(value).toBe(userConfigValue);
});
});
describe('SlateConfig.set()', () => {
test('sets the value of a config for a given key', () => {
const SlateConfig = require('../index');
const config = new SlateConfig(schema);
config.set('some.new.key', 'someNewValue');
expect(config.schema['some.new.key']).toBe('someNewValue');
});
test('throws an error if key has already been set, unless override boolean has been explicitely set', () => {
const SlateConfig = require('../index');
const config = new SlateConfig(schema);
expect(() => config.set('some.key', 'someOtherValue')).toThrowError();
});
});
const path = require('path');
const fs = require('fs');
const themeDirectory = fs.realpathSync(process.cwd());
// Fetch the contents of Slate's user config once, and only once
global.slateUserConfig = global.slateUserConfig || getSlateUserConfig();
function getSlateConfig() {
try {
const slateRcPath = resolveTheme('slate.config.js');
return require(slateRcPath);
} catch (error) {
return {};
module.exports = class SlateConfig {
constructor(schema, userConfigOverride) {
if (typeof schema === 'undefined') {
throw new TypeError(
'[slate-config]: A schema object must be provided as the first argument',
);
}
this.userConfigOverride = userConfigOverride;
this.schema = Object.assign({}, schema);
}
}
function generate(schema, slaterc = getSlateConfig()) {
// Creates a config object of default or slaterc values
const config = _generateConfig([schema], slaterc)[schema.id];
config.__schema = schema;
get userConfig() {
return global.slateUserConfig;
}
return config;
}
set(key, value, override = false) {
if (typeof this.schema[key] !== 'undefined' && !override) {
throw new Error(
`[slate-config]: A value for '${key}' has already been set. A value can only be set once.`,
);
}
function _generateConfig(items, overrides) {
const config = {};
this.schema[key] = value;
}
items.forEach((item) => {
if (Array.isArray(item.items)) {
config[item.id] = _generateConfig(
item.items,
overrides && overrides[item.id],
get(key) {
const defaultValue = this.schema[key];
const userConfigValue = this.userConfig[key];
let computedDefaultValue;
if (
typeof defaultValue === 'undefined' &&
typeof userConfigValue === 'undefined'
) {
throw new Error(
`[slate-config]: A value has not been defined for the key '${key}'`,
);
} else if (overrides && typeof overrides[item.id] !== 'undefined') {
config[item.id] = overrides[item.id];
} else if (typeof item.default !== 'undefined') {
config[item.id] = item.default;
}
});
return config;
}
if (typeof defaultValue === 'function') {
computedDefaultValue = defaultValue(this);
} else {
computedDefaultValue = defaultValue;
}
function resolveTheme(relativePath) {
return path.resolve(themeDirectory, relativePath);
}
if (typeof userConfigValue === 'undefined') {
return computedDefaultValue;
} else if (typeof userConfigValue === 'function') {
return userConfigValue(this, computedDefaultValue);
} else {
return userConfigValue;
}
}
};
function resolveSelf(relativePath) {
return path.resolve(__dirname, relativePath);
function getSlateUserConfig() {
const slateConfigPath =
global.slateConfigPath || path.join(process.cwd(), 'slate.config.js');
if (fs.existsSync(slateConfigPath)) {
return require(slateConfigPath);
} else {
return {};
}
}
module.exports = {
generate,
resolveTheme,
resolveSelf,
getSlateConfig,
};
{
"name": "@shopify/slate-config",
"version": "1.0.0-beta.7",
"version": "1.0.0-beta.8",
"description": "Generate configurations for Slate packages by applying values from slate.config.js to override default values",

@@ -5,0 +5,0 @@ "main": "index.js",

# @shopify/slate-config
Generates configurations for Slate packages by applying values from `slate.config.js` to override default values.
Handles all of Slate's configurable options. Slate packages can declare options in schema files and access the default values of these options in their code. Slate Config will also look for a `slate.config.js` file in the root of a Slate theme project and use any key/values declared in it to override default values declared in Slate packages.
An example config file from `@shopify/slate-babel`:
### Example Schema
The format of Slate configurations relies on a flat object structure using unique keys to access each of its configuration items.
```js
const slateConfig = require('@shopify/slate-config');
const path = require('path');
const commonPaths = require('@shopify/slate-config/common/paths.schema');
module.exports = slateConfig.generate({
items: [
{
id: 'babelLoaderEnable',
default: true,
description: 'Enable/disable Babel for theme scripts',
type: 'boolean',
},
],
});
```
module.exports = {
// You can divide your config items across multiple files, import them, and then
// spread them into your main config schema. Slate Config comes with common
// config items like paths which might be used in any Slate package.
...commonPaths
`slate.config.js` could override the default value of `babelLoaderEnable` by declaring the following:
// Configuration values can be set directly
'webpack.babel.enable': true,
```js
module.exports = {
babelLoaderEnable: false,
// Or computed when the schema file is require()'d by another file
'paths.theme': process.cwd(),
// Or even computed when requested by Slate Config's .get() method. This allows
// configuration items to access other configuration items. The argument passed
// to the function is an instance of the SlateConfig() class.
'paths.theme.src': config => path.join(config.get('paths.theme'), 'src'),
};
```
### Methods
#### SlateConfig(schema)
The main class constructor which consumes a config schema and returns a config instance.
```
const SlateConfig = require('@shopify/slate-config');
const config = new SlateConfig(require('../../../slate-tools.schema'));
```
#### SlateConfig.prototype.get(key)
Fetches the value of a given config item, and throws an error if the item does not exist
```
const themeRoot = config.get('paths.theme');
```
#### SlateConfig.prototype.set(key, value, override)
Sets the value of config item, and throws an error in the key already has a value set to it, unless the override boolean is set to true.
```
config.set('newKey', 'newValue'); // Sets new key in your config instance
config.set('existingKey', 'someValue'); // Throws an error because key already exists
config.set('existingKey', 'newValue', true); // Doesn't throw an error because override is set to true
```
### Testing
Generally, it's considered a best practice to add unit tests to any configuration items you use in your code to catch any future unintentional regressions. To test a configuration item, you can modify the global object used to normally store `slate.config.js` values:
```
global.slateUserConfig['paths.theme'] = 'some/new/path/for/testing'
```
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc