https://github.com/ozum/script-helper
Helper for creating and maintaining boilerplates, configurations and script modules for npm projects
https://github.com/ozum/script-helper
Last synced: about 2 months ago
JSON representation
Helper for creating and maintaining boilerplates, configurations and script modules for npm projects
- Host: GitHub
- URL: https://github.com/ozum/script-helper
- Owner: ozum
- License: other
- Created: 2018-04-26T13:14:35.000Z (about 7 years ago)
- Default Branch: master
- Last Pushed: 2018-07-11T11:21:03.000Z (almost 7 years ago)
- Last Synced: 2024-10-29T23:34:58.574Z (7 months ago)
- Language: TypeScript
- Size: 781 KB
- Stars: 1
- Watchers: 2
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.hbs
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# script-helper
Helper for creating and maintaining boilerplates, configurations and script modules for npm projects.
[](http://commitizen.github.io/cz-cli/)
# Description
Provides utility class and related methods to create script modules which manipulate npm projects such as create/copy/remove files, directories and data files.
Also provides `reset()` method which reverses all modifications made by this module.Script modules help to develop npm modules without config. A very good article explaining this concept is written by Kent C. Dodds which can be found
[here](https://blog.kentcdodds.com/automation-without-config-412ab5e47229).With `script-helper`, it is very easy to cerate custom script modules.
# Inspired
Inspired by [kcd-scripts](https://github.com/kentcdodds/kcd-scripts) utility functions, thanks to Kent C. Dodds and all [contributors](https://github.com/kentcdodds/kcd-scripts#contributors).
# Synopsis
## Module Hierarchy
```
┏ 'project' module: npm project.
┣━━ 'my-scripts' module: Your custom scripts module to manipulate npm projects. Uses `script-helper` (this module).
┣━━━━ 'script-helper' module: This module.
```## Configuration
Configuration is based on [cosmiconfig](https://www.npmjs.com/package/cosmiconfig). See below for details.
## 'my-scripts' module
In examples below, it is assumed that your scripts module is named and uploaded to npm as `my-scripts`. You can use name you choose.
### my-scripts/package.json
```json
{
"bin": { "my-scripts": "lib/index.js" },
"scripts": {
"postinstall": "my-scripts init",
"preuninstall": "my-scripts reset",
"test": "my-scripts test"
}
}
```### my-scripts/lib/index.js
```js
#!/usr/bin/env nodeconst { Project, execute } = require("script-helper");
const project = new Project();
module.exports = project; // If you don't want to use execute() helper, you can access exported project via require.// If called from directly from CLI
if (require.main === module) {
try {
const result = project.executeFromCLISync(); // Optional helper which executes scripts in 'scripts' directory, which is in same directory with this file.
} catch (e) {
console.error(e);
process.exit(1);
}
}
````project.executeFromCLISync()` takes script name to execute and arguments from `process.argv` and requires your script and executes it's exported `script()` function, and passes 3 parameters.
1. `project`: {@link Project} instance, to help tasks related to project module.
1. `args`: args passed from CLI.
1. `s`: {@link ScriptKit} instance, to help tasks related to script file which will be executed.### my-scripts/lib/scripts/init.js
```js
function script(project, args, s) {
// Reset previous modifications
project.reset();// Add some scripts to package.json. (Does not overwrite if target keys are already defined or changed by user.)
// You can change other keys by hand.
project.package
.set("scripts.test", "my-scripts test")
.set("scripts.compile", "my-scripts compile");// Create a .env file with default content. (Does not overwirte if it is already created or changed by user.)
project.writeFile(".env", "PASSWORD=my-super-secret-password");// Create a symlink in project which points to a file in your custom module.
// project/tsconfig.json -> project/node_modules/my-scripts/lib/config/tsconfig.json
project.createSymLink("lib/config/tsconfig.json", "tsconfig.json");// Copy a file from your custom module to project.
// Copy: project/node_modules/my-scripts/lib/config/changelog.md -> project/CHANGELOG.md
project.copyFile("lib/config/changelog.md", "CHANGELOG.md");
}// Function to be called must be exported under 'script' key if you use 'execute' helper.
module.exports = { script };
```### my-scripts/lib/scripts/reset.js
```js
function script(project, args, s) {
// Reset all created files, symlinks, changed JSON and YAML entries if they are not changed by user.
// All modifications are tracked in a JSON file called 'my-scripts-registry.json'
// 'my-scripts' is the name of your module and file name is shaped according the name of your module.
project.reset();
}module.exports = { script };
```### my-scripts/lib/scripts/build/index.js
```js
function script(project, args, s) {
// s is ScriptKit instance, see API doc.
const subScript = project.isTypeScript ? "tsc" : "babel";// Executes my-scripts/lib/scripts/build/tsc.js or my-scripts/lib/scripts/build/babel.js
return s.executeSubScriptSync(subScript, args);
}module.exports = { script };
```### my-scripts/lib/scripts/build/tsc.js
```js
function script(project, args, s) {
// Execute some commands serially and concurrently. Commands in object is executed concurrently.
// In example below, `serial-command-1` is executed first, then `serial-command-2` is executed, then based on condition `serial-command-3` is executed or not,
// `build-doc-command`, `some-other-command` and `tsc` is executed using `concurrently` module (Keys are names used in log).
// Lastly `other-serial-command` is executed. If some command in serial tasks fails, no further command is executed and function would return.
return project.executeSync(
["serial-command-1", ["arg"]],
"serial-command-2",
someCondition ? "serial-command-3" : null,
{
my-parallel-job: ["build-doc-command", ["arg"],
my-parallel-task: "some-other-command"
builder: ["tsc", ["arg"],
},
["other-serial-command", ["arg"]],
);
}module.exports = { script };
```### my-scripts/lib/scripts/test.js
```js
process.env.BABEL_ENV = "test";
process.env.NODE_ENV = "test";function script(project, args, s) {
const config = []; // Some default config
require("jest").run(config);
return { status: 0, exit: false };
};module.exports = { script };
```and so on...
## npm project module
### package.json
Instead of adding scripts below manually, you can create an init script and add it to `postinstall` (see init example above)
```json
{
"scripts": {
"test": "my-scripts test",
}
}
``````
> npm testor
> node_modules/.bin/my-scripts test
```# Configuration
Your scripts module (i.e. `my-scripts`) has builtin [cosmiconfig](https://www.npmjs.com/package/cosmiconfig) support. If user puts a cosmiconfig compatibale configuration in npm project,
you can access configration via `project.config()` method in your script functions.If script module contains user names such as `@microsoft/typescript`, cosmiconfig name is converted to dashed version: `microsoft-typescript`.
By default you can design your own configuration schema. `script-helper` provides some defaults and related methods, as described below:
|Key|Type|Method|Description|
|---|---|---|---|
|`optIn`|`Array.`|`project.isOptedIn()`|Lists opted in options.|
|`optOut`|`Array.`|`project.isOptedOut()`|Lists opted out options.|# Highlights
* Tries best for non-destructive modifications.
* Tracks all modifications in a registry json file.
* Provides `project.reset()` method to reset all changes made by this module.
* Changes in JSON and YAML files are tracked by key level using [resettable](https://www.npmjs.com/package/resettable).
* User created keys and values would not be deleted/modified if `{ force: true }` ıs not used.
* User can further modify data files. They do not interfere with this module's targets.
* CAVEAT: [resettable](https://www.npmjs.com/package/resettable) cannot handle all cases, please see it's documentation and always use version control.
* Changes in non data files are tracked using SHA-1 hash.
* JSON, YAML and JavaScript files are normalized in memory before comparison to eliminate white-space noise.
* Provides `execute()` helper function to execute your scripts and handle errors and saving project data files and registry.
* Provides `project.save()` method for saving registry json file and changes made to data files.## Notes
* For tracking data files by key level, use `project.readDataFile()` method, which returns [DataObject](#dataobject)
* Methods of [DataObject](#dataobject) such as `set()`, `remove()` provides automatic log messages.
* You can directly access data using `.data` property.
* Tracking still works if you manipulate data from `.data` directly, because modifications are calculated during file save.
* All data files read with `project.readDataFile()` are saved during project save (`project.save()`).
* Do not use `project.writeFile()` for those files.
* If user modifies file or data created by this library, they are not modified further if not forced with `{ force: true }` option.
* DO NOT forget `project.save()` after you finish your modifications.
* Or use `execute()` helper function, which saves project even after error in your scripts, and handle `process.exit()` as necessary.
* `reset()` method does not recreate deleted files and directories.### Disable Tracking
To completely disable tracking, set `track` to `false`in constructor:
```js
const project = new Project({ track: false });
```Tracking may be enabled/disabled per operation:
```
project.writeFile("some.txt", "content", { track: false });
```Please note that disabling tracking does not permit automatically modifying user created files / content.
`force` option is still needed to overwrite. However non-tracked modifications are treated like user created content.For example:
```js
// Assume user.txt is created manually by user beforehand.
project.deleteFile("user.txt"); // NOT deleted, because it is created by user.
project.writeFile("auto.txt", "x"); // Created and tracked. It is known this file is created by this library.
project.deleteFile("auto.txt"); // Deleted, because it is known that file was created by this library.
project.writeFile("non-tracked.txt", "x", { track: false }); // Created and tracked. It is known this file is created by this library.
project.deleteFile("non-tracked.txt"); // NOT deleted, because it is not tracked, and it is unknown created by whom.
project.deleteFile("non-tracked.txt", { force: true }); // Deleted, because `force` is in effect.
```# API
{{>main~}}