Skip to content
This repository has been archived by the owner on Nov 21, 2019. It is now read-only.

Single source of truth distributes shared config between layers, allowing extension #17

Merged
merged 115 commits into from Nov 28, 2018
Merged
Show file tree
Hide file tree
Changes from 103 commits
Commits
Show all changes
115 commits
Select commit Hold shift + click to select a range
1341073
Generate sass breakpoints file from a json config object
davidcmoulton Oct 12, 2018
6829588
Turn config generation script into a module
davidcmoulton Oct 12, 2018
3c57c7f
Tidy up json config
davidcmoulton Oct 12, 2018
918c53b
Add gulp task to run the script to distribute shared config
davidcmoulton Oct 12, 2018
b1c038a
Remove console logging
davidcmoulton Oct 12, 2018
ea14b36
Finish the sass breakpoint file with a new line
davidcmoulton Oct 12, 2018
64e9f7a
Add incorporateSharedConfig task into default Gulp workflow
davidcmoulton Oct 12, 2018
4d78ca4
Merge branch 'master' into shared-config
davidcmoulton Oct 16, 2018
9fd4da1
Make distribute-shared-config.js more testable
davidcmoulton Oct 16, 2018
4da28c9
Add chai dependency
davidcmoulton Oct 16, 2018
704a17f
Initial tests for distribute-shared-config.js
davidcmoulton Oct 16, 2018
ce9ffcf
Update test wording
davidcmoulton Oct 16, 2018
f69eed0
Add function to format breakpoint config for js
davidcmoulton Oct 16, 2018
b7300f2
Write js-formatted breakpoint config to file
davidcmoulton Oct 16, 2018
468ee6e
Refactor to remove duplication
davidcmoulton Oct 16, 2018
168ae14
Tidy
davidcmoulton Oct 16, 2018
595f7c0
Add empty line to end of autogenerated js breakpoints
davidcmoulton Oct 16, 2018
dd8ca1e
Refactor to smaller functions
davidcmoulton Oct 16, 2018
cdde977
Tidy tidy
davidcmoulton Oct 16, 2018
ddd2928
Refactor task name
davidcmoulton Oct 16, 2018
92f7a68
Revert breakpoint names to previous version (less good), fix separately
davidcmoulton Oct 16, 2018
8d2642d
Remove 'autogenerated' prefix from derived config file names
davidcmoulton Oct 16, 2018
ecce35c
Add autogenerated js breakpoint config
davidcmoulton Oct 16, 2018
817c64a
Return a Promise from async function
davidcmoulton Oct 17, 2018
a348557
Refactor writeFile to use Promises & extract reporting
davidcmoulton Oct 17, 2018
5c1e667
Promises all the way down
davidcmoulton Oct 18, 2018
1a41085
Log errors to console
davidcmoulton Oct 18, 2018
9fabaa9
Return promise from distribute(), enabling callback handling in gulpfile
davidcmoulton Oct 18, 2018
4b67b5e
Catch Promise rejection
davidcmoulton Oct 18, 2018
8540b54
Format code for clarity
davidcmoulton Oct 18, 2018
e8f7782
More tests
davidcmoulton Oct 18, 2018
65b8c7b
Typo
davidcmoulton Oct 18, 2018
9b18082
Rethrow
davidcmoulton Oct 18, 2018
dadc387
Make sure gulp process exits with 1 if config distribution fails
davidcmoulton Oct 18, 2018
cf6dc69
Take advantage of auto Promise wrapping magic
davidcmoulton Oct 18, 2018
f1033c7
Add dependencies for describing config in yaml
davidcmoulton Oct 18, 2018
f0b0809
Change to yaml for config description
davidcmoulton Oct 19, 2018
e3cb7f2
Remove unused dependency
davidcmoulton Oct 19, 2018
576c4ce
Tidy
davidcmoulton Oct 19, 2018
b2417d4
Log caught errors
davidcmoulton Oct 29, 2018
b9fa354
Caesar's to caesar, errors to error log
davidcmoulton Oct 29, 2018
38fcfa1
Sample config files of putative approach to customisable js-based config
davidcmoulton Nov 8, 2018
829d17f
Refactor config to make clearer there's only one config object involved
davidcmoulton Nov 8, 2018
328f837
Allow explicitly assigning config data properties to technology layers
davidcmoulton Nov 8, 2018
b6fe2c2
Whitespace
davidcmoulton Nov 8, 2018
4f0afb6
Report path of config file used
davidcmoulton Nov 9, 2018
a996752
Process config for sass generically now, not just explicitly breakpoints
davidcmoulton Nov 9, 2018
bd4be20
Write sass variables generically based on config, not just breakpoints
davidcmoulton Nov 9, 2018
ca15ea4
Remove obsolete tests
davidcmoulton Nov 9, 2018
a0e6b3d
Only run directly if loaded as the main module a la Python
davidcmoulton Nov 9, 2018
7cafe05
Fix bugs in Gulp generation of sass config
davidcmoulton Nov 9, 2018
d25a934
Decouple to allow arbitrary # config files
davidcmoulton Nov 12, 2018
4d22ec8
Update shared config distribution to use new config model
davidcmoulton Nov 12, 2018
c91f9c9
Remove spurious logging
davidcmoulton Nov 12, 2018
ba2e2a5
Preserve Color objects when deep merging
davidcmoulton Nov 12, 2018
7f1312d
Remove PropertyLayerAllocator class: not needed
davidcmoulton Nov 12, 2018
d995c17
Rename base config to libero config
davidcmoulton Nov 12, 2018
5159f90
Tidy Gulp file
davidcmoulton Nov 12, 2018
80d5839
Implement full eLife colour palette as config properties
davidcmoulton Nov 12, 2018
e4e0124
Distribute config to js
davidcmoulton Nov 14, 2018
36fec82
Implement deferred config setting using jexl & expressions
davidcmoulton Nov 14, 2018
40d7ddd
Extract isMergeableObject
davidcmoulton Nov 19, 2018
dd892e7
Remove some console logging
davidcmoulton Nov 19, 2018
3516df4
Pass allConfigs to generateConfig to ease testing
davidcmoulton Nov 19, 2018
976e61a
Rename file that generates config to be more accurate
davidcmoulton Nov 19, 2018
67750fa
Refactor rename
davidcmoulton Nov 19, 2018
8301116
Add test for processDeferredConfig method of configGenerator
davidcmoulton Nov 20, 2018
87b0bc3
Add more tests
davidcmoulton Nov 20, 2018
245f842
Improve readability of test narrative
davidcmoulton Nov 20, 2018
8eafdf0
Fix tests testing functions that return Promises
davidcmoulton Nov 20, 2018
c827011
Remove unused import
davidcmoulton Nov 20, 2018
3ec46d1
Remove stand-alone execution ability of configGenerator module
davidcmoulton Nov 20, 2018
2a655be
Minor tidy
davidcmoulton Nov 21, 2018
48e2f4c
Refactor config generator into a class
davidcmoulton Nov 21, 2018
140f4eb
Make more usable
davidcmoulton Nov 21, 2018
430021b
Refactor structure & update invocation
davidcmoulton Nov 21, 2018
e2b93ac
Extract test fixtures
davidcmoulton Nov 21, 2018
bbc8010
Tidy
davidcmoulton Nov 22, 2018
54842b8
Even a bit tidier
davidcmoulton Nov 22, 2018
bd7ec6c
Extend tests
davidcmoulton Nov 22, 2018
a5c3ce5
Extend tests, with appropriate supporting refactoring
davidcmoulton Nov 23, 2018
36ea903
Make ConfigDistributor into a class & update tests
davidcmoulton Nov 23, 2018
2974d2c
Update comments & weed files
davidcmoulton Nov 23, 2018
5ad6ac4
Remove obsolete files
davidcmoulton Nov 23, 2018
5dabe74
Add files generated by running the config generator
davidcmoulton Nov 23, 2018
d5808ed
Remove extraneous empty lines
davidcmoulton Nov 23, 2018
8e6d0bf
Bring sass into line with name changes required for config spec & tidy
davidcmoulton Nov 26, 2018
333f94a
Move derived config to dedicated locations
davidcmoulton Nov 26, 2018
b9e18eb
Move derived config to dedicated locations and don't version control
davidcmoulton Nov 26, 2018
1abf3ea
Make config-derived output locations consistent with & without Gulp
davidcmoulton Nov 26, 2018
d9b597e
Extract config path specification into json file
davidcmoulton Nov 26, 2018
fb8f8ca
Add Gulp task to clean derived config files
davidcmoulton Nov 26, 2018
edd6110
Extract distributeSharedConfig from build within Gulp run sequence
davidcmoulton Nov 26, 2018
2bff773
Add ./bin/dev quickstart utility script
davidcmoulton Nov 26, 2018
96dce20
Tweak how Docker runs Gulp in CI to accommodate config generation
davidcmoulton Nov 26, 2018
4478d0e
Bump Docker node version to match engine requirements
davidcmoulton Nov 26, 2018
886a026
Delete example of additional config
davidcmoulton Nov 27, 2018
cdfb87a
Update comments
davidcmoulton Nov 27, 2018
6f49424
Update README with details of the configuration system
davidcmoulton Nov 27, 2018
72a7787
Rename file
davidcmoulton Nov 27, 2018
5cc541c
Rename another file
davidcmoulton Nov 27, 2018
faf32b8
Udpate README
davidcmoulton Nov 27, 2018
9304432
Typos
davidcmoulton Nov 27, 2018
0fd67da
Remove ./bin/dev for now
davidcmoulton Nov 27, 2018
a3181df
Update dependencies
davidcmoulton Nov 27, 2018
84668ab
Indentation
davidcmoulton Nov 27, 2018
8e07413
Simplify path for js derived config
davidcmoulton Nov 27, 2018
122989f
Specify sass font variables using the config system
davidcmoulton Nov 27, 2018
dc169c7
Generate all sass variable files using config system & git ignore them
davidcmoulton Nov 27, 2018
9c71d31
Update README
davidcmoulton Nov 27, 2018
119c9c5
Update js config filename
davidcmoulton Nov 27, 2018
3f6f103
Update Gulp with new derived config paths
davidcmoulton Nov 27, 2018
df13d5d
Move config distribution into main Gulp process
davidcmoulton Nov 27, 2018
9a41b66
Remove mention of extending config until decided
davidcmoulton Nov 28, 2018
6a0fbc0
Update font variable name
davidcmoulton Nov 28, 2018
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Expand Up @@ -8,3 +8,5 @@
/source/css/*.map
/source/styleguide/
/vendor/
/source/css/sass/derived-from-config/
/source/js/derived-from-config/
4 changes: 2 additions & 2 deletions Dockerfile
@@ -1,8 +1,8 @@
FROM node:8.12.0-slim
FROM node:10.12.0-slim
WORKDIR /patterns-core

COPY package.json package-lock.json ./
RUN npm install

COPY . ./
RUN node_modules/.bin/gulp build
RUN node_modules/.bin/gulp assemble
116 changes: 112 additions & 4 deletions README.md
@@ -1,8 +1,116 @@
Libero pattern library
======================

## Pipeline
Libero pattern library
======================

## Developing patterns with the pattern library
[This section is a work in progress. Likely to be updated to use Docker.]
To get things up and running for local development using default configuration, run `/bin/dev`.

### Configuration
N.B. When configuration files are changed, the config needs to be regenerated, either by running `node ./libero-config/bin/distributeConfig.js` or the Gulp task `distributeConfig`.

#### Uses of configuration
Configuration is used for two things:

1. to be the single source of truth for knowledge that needs to be shared across across front end technology boundaries. For example, media query breakpoint values need to exist in the styling layer, but they are also often needed by JavaScript. Note that for flexibility all configuration could be maintained using this system in order to make it easier to later distribute configuration if it suddenly becomes necessary.

1. whilst enabling the single source of truth, configuration must also be able to be changed as required in a manageable way. The breakpoints, colors, baseline grid measures etc may not be the same between implementations, whilst enabling the ability to easily reuse most of the config and only tweak the odd value if a light touch is needed.

#### Anatomy of configuration
(All config file code examples are taken from `/libero-config/config--libero-default.js`.)

The build process uses a Node.js container image to build all assets, and copy them out of the container into `export/`.
##### Simple example
`config.data` is where you define your configuration data.
Here `config.data` defines the the `small` and `medium` site breakpoints:

```
config.data.breakpoint = {site: {}};
config.data.breakpoint.site.small = 480;
config.data.breakpoint.site.medium = 730;
```

`config.layerAllocations` specifies which technology layers the properties of `config.data` are distributed to. Continuing the above example:
```
config.layerAllocations = {
sass: ['breakpoint'],
js: ['breakpoint'],
template: ['breakpoint'] };
```
specifies that the `breakpoint` config must be distributed to all three available layers: the sass, JavaScript and the templating layer.

##### Advanced example
Sometimes configuration values depend on other configuration values, for example measures in a grid system. To be able to maintain these relationships even when the underlying predicate value may be modified by a later-loading config file, the calculation of the final value determined by these relationships must be deferred until all specified configurations are loaded and parsed. This is achieved by specifying these simple mathematical expressions in the format:
```
'!expression [some simple mathematical expression]'
```
Using this we can specify the baseline grid as:
```
config.data.baselinegrid = {space: {}};
config.data.baselinegrid.space.extra_small_in_px = 12;
config.data.baselinegrid.space.small_in_px = '!expression baselinegrid.space.extra_small_in_px * 2';
config.data.baselinegrid.space.smallish_in_px = '!expression baselinegrid.space.small_in_px * 1.5';
config.data.baselinegrid.space.medium_in_px = '!expression baselinegrid.space.small_in_px * 2';
...
```
The result is that `config.data.baselinegrid.space.small_in_px` will have the value twice that of whatever the final value of `config.data.baselinegrid.space.extra_small_in_px`is, *even if `config.data.baselinegrid.space.extra_small_in_px` is modified by a later loading config*. This provides a way of reusing the essentials of the baseline grid system, but basing it on a different key value as required.

#### Distributing configuration
##### Distributing to SASS
Each property of `config.data` specified in `config.layerAllocations.sass` is eventually written as a SASS file to `/source/css/sass/derived-from-config/_variables--[propertyname].sass`. Each of these files contains the SASS variables describing the config for that property. Looking at the `breakpoint` example again, this config
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be generating all the 'variables' files? If so, /source/css/sass/_variables--[propertyname].sass or /source/css/sass/variables/[propertyname].sass?

(Also, sass inside css?)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Originally I though that it'd only need to generate those that are either extended by additional config, or that contain knowledge shared between technology layers, but now I'm thinking that it should generate all of them.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sass inside css because of how it's used during the export process. I'm not wedded to that structure, but a change is outside the scope of this PR.


```
// specified in a config file
config.data.breakpoint = {site: {}};
config.data.breakpoint.site.small = 480;
config.data.breakpoint.site.medium = 730;

config.layerAllocations.sass = ['breakpoint'];
```

generates this file:
```
// /source/css/sass/derived-from-config/_variables--breakpoint.sass
$breakpoint-site-small: 480;
$breakpoint-site-medium: 730;
```
##### Distributing to JavaScript
Each property of `config.data` specified in `config.layerAllocations.jss` is eventually written to `/source/js/derived-from-config/configForJs.json`. Looking at the `breakpoint` example again, this config:

```js
// specified in a config file
config.data.breakpoint = {site: {}};
config.data.breakpoint.site.small = 480;
config.data.breakpoint.site.medium = 730;
config.layerAllocations.js = ['breakpoint'];
```

adds this into `configForJs.json`:
```
// /source/js/derived-from-config/configForJs.json
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty wordy, /source/js/config.json?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair point. I like/source/js/derivedConfig.json: it indicates that the file shouldn't be directly edited, and we can't insert that information as a comment into the file itself as it's json.

...
{"breakpoint":{"site":{"small":480,"medium":730}}}
...
```
##### Distributing to templates
[Not yet implemented]

#### Modifying configuration
##### Default configuration
Default configuration is supplied by `/libero-config/config--libero-default.js`. If this is sufficient, the rest of this section may be safely ignored.

##### Changing configuration
Do not change the contents of the default config file `/libero-config/config--libero-default.js` directly.

Any changes to the configuration should be effected by placing one or more custom configuration files into `/libero-config/`, and registering the file name(s) in `/libero-config/configRegister.json`. Any files listed here are loaded as config files. The order of the files listed defines their load order. This is important when namespace clashes occur: when this happens the clashing name that was loaded last wins. This is how specific configuration properties are overridden.

##### Swapping out configuration wholesale
Supply your own config file(s), add appropriate references to `/libero-config/configRegister.js`, and remove mention of `configs--libero-default.js` from `/libero-config/configRegister.js`.

##### Keep default configuration but augment or override some of its properties
Supply your own config file(s), add appropriate references to `/libero-config/configRegister.js`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably don't actually want to support this? We know that we want to support extensions of the pattern library, but forking isn't really the way.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something to think about. Not sure just at the moment how else we might handle extensions. Will ponder.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will remove documentation, but leave implementation in for now, undocumented & so subject to change.


## Pipeline

The build process uses a Node.js container image to build all assets, and copy them out of the container into `export/`.

`export/` can then be packaged to be released on Github, or reused elsewhere.
34 changes: 34 additions & 0 deletions bin/dev
@@ -0,0 +1,34 @@
#!/usr/bin/env bash
# First time...
if [ ! -d ./public ]; then
echo -e "Setting up for the first time...";
npm install
mkdir ./public
cp -r ./core/styleguide ./public/
fi

PORT=${1:-8090}

echo "Building patterns..."
npx gulp

(while true; do
node_modules/.bin/gulp watch && sleep 5
done) & pid=$!
PID_LIST+=" $pid";

# Need to generate before watch to ensure images are in the right place.
php ./core/builder.php --generate
thewilkybarkid marked this conversation as resolved.
Show resolved Hide resolved

php ./core/builder.php --watch > /dev/null 2>&1 & pid=$!
PID_LIST+=" $pid";

php -S 0.0.0.0:${PORT} -t ./public > /dev/null 2>&1 & pid=$!
PID_LIST+=" $pid";

trap "kill ${PID_LIST}" SIGINT

echo "Running Pattern Library at http://localhost:${PORT}/";
wait ${PID_LIST}
echo
echo "Stopping pattern library, gulp and server.";
30 changes: 29 additions & 1 deletion gulpfile.js
@@ -1,6 +1,7 @@
'use strict';

const del = require('del');
const {exec} = require('child_process');
const flatten = require('gulp-flatten');
const gulp = require('gulp');
const mergeStreams = require('merge-stream');
Expand Down Expand Up @@ -46,6 +47,7 @@ function buildConfig(invocationArgs, sourceRoot, testRoot, exportRoot) {
config.dir.src.images = `${config.sourceRoot}images/`;
config.dir.src.fonts = `${config.sourceRoot}fonts/`;
config.dir.src.templates = `${config.sourceRoot}_patterns/`;
config.dir.src.js = `${config.sourceRoot}js/`;

config.dir.test.sass = `${config.testRoot}sass/`;

Expand All @@ -70,6 +72,10 @@ function buildConfig(invocationArgs, sourceRoot, testRoot, exportRoot) {
config.files.src.images = [`${config.dir.src.images}/*`, `${config.dir.src.images}/**/*`];
config.files.src.fonts = [`${config.dir.src.fonts}/*`, `${config.dir.src.fonts}/**/*`];
config.files.src.templates = [`${config.dir.src.templates}/*.twig`, `${config.dir.src.templates}/**/*.twig`];
config.files.src.derivedConfigs = [
`${config.dir.src.sass}derived-from-config/**/*`,
`${config.dir.src.js}derived-from-config/**/*`
];

config.files.test.sass = `${config.dir.test.sass}**/*.spec.scss`;
config.files.test.sassTestsEntryPoint = `${config.dir.test.sass}test_sass.js`;
Expand Down Expand Up @@ -151,14 +157,36 @@ gulp.task('exportPatterns', ['patternsExport:clean'], () => {

});

gulp.task('sharedConfig:clean', () => {
return del(config.files.src.derivedConfigs);
});

gulp.task('distributeSharedConfig', ['sharedConfig:clean'], (done) => {
exec('node ./libero-config/bin/distributeConfig.js', (err, stdout, stderr) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can a sub-process be avoided?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possibly. I originally tried to integrate it into the gulp process more effectively, but it didn't work. I think it's time for another go though.

console.log(stdout);
console.log(stderr);
done(err);
});
});

gulp.task('sass:watch', () => {
return gulp.watch([config.files.src.sass, config.files.test.sass], ['css:generate']);
});

gulp.task('default', done => {
gulp.task('assemble', done => {
runSequence(
'distributeSharedConfig',
'build',
done,
);
});

gulp.task('default', done => {
runSequence(
'assemble',
'exportPatterns',
done,
);
});

gulp.task('watch', ['sass:watch']);
127 changes: 127 additions & 0 deletions libero-config/bin/ConfigDistributor.js
@@ -0,0 +1,127 @@
const Color = require('color');
const deepIterator = require('deep-iterator').default;
const flatten = require('flat');
const fs = require('fs');
const path = require('path');
const {promisify} = require('util');

const writeFileAsync = promisify(fs.writeFile);

/**
* Distributes specified config to appropriate layers (sass, js, templates)
* @type {module.ConfigDistributor}
*/
module.exports = class ConfigDistributor {

constructor() {
this.paths = {
out: {
sassVariablesFileNameRoot: '/source/css/sass/derived-from-config/_variables--',
jsonFileName: '/source/js/derived-from-config/configForJs.json'
}
};
}

distribute(configPaths, configGenerator) {

console.log('Distributing config...');

return configGenerator.generateConfig(configPaths)

.then((config) => {
return Promise.all(
[
this.distributeToSass(config.layerAllocations.sass, config.data),
this.distributeToJs(config.layerAllocations.js, config.data),
]
)
})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indentation's a bit hard to read.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'll fix that.


.catch(err => {
console.error(err.message);
process.exit(1);
});
}

distributeToJs(allocations, data) {
return ConfigDistributor.writeFile(
ConfigDistributor.processForJs(allocations, data),
this.paths.out.jsonFileName
);
}

distributeToSass(allocations, data) {

const fileWritePromises = [];

// Each allocation is written to a separate file
allocations.forEach((allocation) => {
const dataForAllocation = {};
dataForAllocation[allocation] = data[allocation];
const processedItemData = ConfigDistributor.processForSass(dataForAllocation);
const outFileName =
`${this.paths.out.sassVariablesFileNameRoot}${allocation}.scss`;
fileWritePromises.push(
new Promise((resolve) => {
resolve(ConfigDistributor.writeFile(processedItemData, outFileName));
})
);
});

return Promise.all(fileWritePromises).catch(err => { throw err; } );

}

static processForJs(allocations, data) {
const processed = {};
allocations.forEach((allocation) => {
processed[allocation] = data[allocation];
});
return JSON.stringify(processed);
}

static processForSass(data) {
const deepData = deepIterator(data);
for (let {parent, key, value} of deepData) {
if (value instanceof Color) {
parent[key] = value.rgb().string();
}
}

return Object.entries(flatten(data, {delimiter: '-'}))
.reduce((carry, pair) => {
const [key, value] = pair;
return `${carry}$${key}: ${value};\n`;
}, '');
}

static writeDirectory(path) {
return new Promise((resolve, reject) => {
fs.mkdir(path, { recursive: true}, (err) => {
if (err) {
reject(err);
}
resolve();
});
});
}

static async writeFile(data, outPath) {
let projectRootPath = process.cwd();
const matched = projectRootPath.match(/^.*\/libero-config\/bin.*$/);
if (matched) {
projectRootPath = path.resolve(path.join(process.cwd(), '../..'));
}
const outPathDirectoryComponent = outPath.substring(0, outPath.lastIndexOf('/') + 1);
const fullDirectoryPath = path.join(projectRootPath, outPathDirectoryComponent);
await this.writeDirectory(fullDirectoryPath);

const filenameComponent = outPath.substring(outPath.lastIndexOf('/') + 1);
return writeFileAsync(path.join(fullDirectoryPath, filenameComponent), data)
.then(() => {
console.log(`Written config to ${path.join(outPathDirectoryComponent, filenameComponent)}`);
})
.catch(err => { throw err });
}

};