@vamship/build-utils 1.0.0-0

Sign up to get free protection for your applications and to get access to all the features.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) [2019] [Vamshi K Ponnapalli]
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,179 @@
1
+ # @vamship/grunt-utils
2
+
3
+ _Library of modules that are designed to construct an opinionated dev/build
4
+ toolchain for Javascript and Typescript projects._
5
+
6
+ > This library was originally intended for internal consumption, though the
7
+ > functionality provided by this library is fairly generic.
8
+
9
+ ## API Documentation
10
+
11
+ API documentation can be found [here](https://vamship.github.io/build-utils).
12
+
13
+ ## Motivation
14
+
15
+ In addition to writing code (and tests!), every project brings with it a common
16
+ set of tasks that comprise a _development workflow_ for the project. This
17
+ workflow includes common activities such as linting, formatting files, testing,
18
+ building, packaging, etc.
19
+
20
+ Having consistent way of performing these tasks makes it easier to switch from
21
+ one project to another, because all common tasks will be identical for a given
22
+ class of project (nodejs library, API server, etc.).
23
+
24
+ In order to ensure this consistency, a common task automation framework (Gulp)
25
+ is used, combined with a consistent configuration and development tool set for
26
+ that framework.
27
+
28
+ This library exports modules and classes that enable the creation of Gulpfiles,
29
+ ensuring that they can be ported from project to project with no changes.
30
+
31
+ All project specific parameters can be declared within a `buildMetdata` property
32
+ in package.json.
33
+
34
+ ## Installation
35
+
36
+ This library can be installed using npm:
37
+
38
+ ```
39
+ npm install @vamship/build-utils
40
+ ```
41
+
42
+ ## Usage
43
+
44
+ This library is intended to be used with a Gulpfile that imports the necessary
45
+ tasks for the build system. The `examples` directory has a sample Gulpfile that
46
+ can be used for any project.
47
+
48
+ ### Environment Variables
49
+
50
+ Setting certain environment variables can alter the behavior of different build
51
+ tasks. The environment variables and their effects are listed below:
52
+
53
+ `BUILD_EXPORT_DOCKER_IMAGE`: When set to `true`, packaging a docker enabled
54
+ project will result in the creation of a tar file called `image-<key>.tar`
55
+ (using `docker save ...`) under the dist directory.
56
+
57
+ `BUILD_DOCKER_REPO`: This environment variable can be used to override the
58
+ docker repository name during package/publish operations.
59
+
60
+ ### Configuring package.json
61
+
62
+ The build system derives its configuration from buildMetadata that is explicitly
63
+ passed to the the project object via the Gulpfile, or by configuring settings
64
+ in the package.json file. We **strongly recommend** the use of package.json to
65
+ configure the build system.
66
+
67
+ Build metadata is identified in `package.json` via a property named
68
+ `buildMetadata`, which is an object that supports the following fields:
69
+
70
+ ```json
71
+ {
72
+ "projectType": "lib|cli|api|aws-microservice|container",
73
+ "language": "ts|js",
74
+ "requiredEnv": [ "...", "..." ],
75
+ "aws": {
76
+ "stacks" {
77
+ "stack-key": "stack-name",
78
+ ...
79
+ }
80
+ },
81
+ "docker": {
82
+ "x86": {
83
+ "repo": "...",
84
+ "buildFile": "Dockerfile"
85
+ "buildArgs": {
86
+ "arg-name": "__ENV__|<value>"
87
+ },
88
+ "isDefault": true,
89
+ },
90
+ "arm64": {
91
+ "repo": "...",
92
+ "buildFile": "Dockerfile.arm64"
93
+ "buildArgs": {
94
+ "arg-name": "__ENV__|<value>"
95
+ }
96
+ }
97
+ }
98
+ }
99
+ ```
100
+
101
+ Note that all properties are not required for all types of projects. Refer to
102
+ the package.json files in the `examples` directory to get an idea of typical
103
+ configuration for different project types.
104
+
105
+ #### Upgrading to v0.3x
106
+
107
+ Starting from version 0.3, the docker build configuration supports multiple
108
+ targets, meaning that the application may be used to generate different docker
109
+ images, typically intended for different processor architectures. While the
110
+ changes are backwards compatible with 0.2.x, a deprecation warning will be
111
+ posted if the old style configuration is specified.
112
+
113
+ In order to upgrade to v 0.3 and above, the docker section should be updated
114
+ from
115
+
116
+ ```
117
+ "docker": {
118
+ "repo": "...",
119
+ "registry": "Dockerfile"
120
+ "buildArgs": {
121
+ "arg-name": "__ENV__|<value>"
122
+ },
123
+ }
124
+ ```
125
+
126
+ to:
127
+
128
+ ```
129
+ "docker": {
130
+ "default": {
131
+ "repo": "...",
132
+ "buildFile": "Dockerfile"
133
+ "buildArgs": {
134
+ "arg-name": "__ENV__|<value>"
135
+ }
136
+ }
137
+ }
138
+ ```
139
+
140
+ The basic change is that the build configuration is specified as the value of a
141
+ build target key.
142
+
143
+ Note that the `docker.registry` parameter is no longer supported in the new
144
+ configuration format.
145
+
146
+ #### Properties:
147
+
148
+ `projectType`: Identifies the project type, which in turn determines the build
149
+ tasks generated by the system.
150
+
151
+ `language`: The project language - javascript or typescript.
152
+
153
+ `requiredEnv`: A list of environment variables that must be defined prior to some
154
+ tasks, such as publishing AWS projects, packaging docker images, publishing
155
+ private npm repositories, etc. The build system only checks for the existinence
156
+ of these variables, but does not care about their values.
157
+
158
+ `aws`: This object must be specified for aws microservice projects.
159
+ `aws.stacks`: This sub object defines a list of cloud formation stacks published
160
+ by the project. The keys represent the names that will be used for the gulp
161
+ tasks, and the values are the names of the cloud formation stacks.
162
+
163
+ `docker`: This object must be sepcified for projects that result in docker
164
+ images. This could apply to multiple project types including `cli`, `api` and
165
+ `container`. The docker configuration can specify multiple targets with
166
+ different docker files and build arguments. Each target has a name and a
167
+ corresponding set of configuration options. The name of the target can be any
168
+ string, except that a target named "default" is treated as the default packaging
169
+ target.
170
+
171
+ Each target can have the following properties:
172
+
173
+ `[target].repo`: A fully qualified repo name to be used to publish docker images.
174
+ `[target].buildFile`: The name of the docker file to use for the build. If
175
+ omitted, will default to "Dockerfile"
176
+ `[target].buildArgs`: This is a map of build arguments that will be passed to
177
+ docker at build time. The keys are the build argument names, and the values are
178
+ the argument values. A special value `__ENV__` indicates that the actual value
179
+ of the build argument must be extracted from the environment.
package/package.json ADDED
@@ -0,0 +1,92 @@
1
+ {
2
+ "name": "@vamship/build-utils",
3
+ "version": "1.0.0-0",
4
+ "description": "Utility library for build tooling",
5
+ "main": "src/index.js",
6
+ "scripts": {
7
+ "clean": "rm -rf .nyc_output coverage",
8
+ "monitor": "nodemon --exec npm run test",
9
+ "test": "nyc mocha -R spec --recursive test/unit/ && nyc report --reporter=html",
10
+ "lint": "eslint src/**/*.js test/**/*.js",
11
+ "format": "prettier --write \"{{src,test}/**/*.js,README.md}\"",
12
+ "docs": "jsdoc --readme README.md --package package.json --template node_modules/docdash --destination docs --recurse src",
13
+ "all": "npm run format && npm run lint && npm run test && npm run clean"
14
+ },
15
+ "files": [
16
+ "package.json",
17
+ "LICENSE",
18
+ "README.md",
19
+ "src/**/*"
20
+ ],
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "https://github.com/vamship/build-utils.git"
24
+ },
25
+ "keywords": [
26
+ "gulp",
27
+ "build",
28
+ "test",
29
+ "utilities"
30
+ ],
31
+ "author": "Vamshi K Ponnapalli <vamshi.ponnapalli@gmail.com>",
32
+ "contributors": [
33
+ {
34
+ "name": "Onaje Baxley",
35
+ "email": "onaje.baxley@gmail.com"
36
+ }
37
+ ],
38
+ "license": "MIT",
39
+ "bugs": {
40
+ "url": "https://github.com/vamship/build-utils/issues"
41
+ },
42
+ "homepage": "https://github.com/vamship/build-utils#readme",
43
+ "devDependencies": {
44
+ "chai": "^4.3.4",
45
+ "chai-as-promised": "^7.1.1",
46
+ "eslint": "^7.32.0",
47
+ "gulp": "^4.0.2",
48
+ "gulp-eslint": "^6.0.0",
49
+ "gulp-jsdoc3": "^3.0.0",
50
+ "gulp-prettier": "^4.0.0",
51
+ "gulp-typedoc": "^3.0.1",
52
+ "gulp-typescript": "^5.0.1",
53
+ "gulp-zip": "^5.1.0",
54
+ "jsdoc": "^3.6.7",
55
+ "mocha": "^9.1.2",
56
+ "nodemon": "^2.0.13",
57
+ "nyc": "^15.1.0",
58
+ "prettier": "^2.4.1",
59
+ "rewire": "^5.0.0",
60
+ "sinon": "^11.1.2",
61
+ "sinon-chai": "^3.7.0"
62
+ },
63
+ "dependencies": {
64
+ "camelcase": "^6.2.0",
65
+ "delete": "^1.1.0",
66
+ "docdash": "^1.2.0",
67
+ "dotenv": "^10.0.0",
68
+ "dotenv-expand": "^5.1.0",
69
+ "execa": "^5.1.1",
70
+ "fancy-log": "^1.3.3",
71
+ "mkdirp": "^1.0.4",
72
+ "semver": "^7.3.5"
73
+ },
74
+ "peerDependencies": {
75
+ "@typescript-eslint/eslint-plugin": ">= ^4.25.0",
76
+ "@typescript-eslint/parser": ">= ^4.25.0",
77
+ "gulp": ">= ^4.0.2",
78
+ "gulp-eslint": ">= ^6.0.0",
79
+ "gulp-jsdoc3": ">= ^3.0.0",
80
+ "gulp-prettier": ">= ^4.0.0",
81
+ "gulp-typedoc": ">= ^3.0.1",
82
+ "gulp-typescript": ">= ^5.0.1",
83
+ "gulp-zip": ">= ^5.1.0",
84
+ "mocha": ">= ^9.1.2",
85
+ "nyc": ">= ^15.1.0",
86
+ "prettier": ">= ^2.4.1"
87
+ },
88
+ "optionalDependencies": {
89
+ "typedoc": ">= ^0.22.5",
90
+ "typescript": ">= ^4.4.4"
91
+ }
92
+ }
@@ -0,0 +1,234 @@
1
+ 'use strict';
2
+
3
+ const _path = require('path');
4
+
5
+ const _sepRegexp = new RegExp(_path.sep.replace(/\\/g, '\\\\'), 'g');
6
+
7
+ /**
8
+ * Abstract representation of a directory, with methods for traversal and
9
+ * glob pattern generation.
10
+ */
11
+ class Directory {
12
+ /**
13
+ * @param {String} path The path represented by this directory.
14
+ */
15
+ constructor(path) {
16
+ if (typeof path !== 'string') {
17
+ throw new Error('Invalid path specified (arg #1)');
18
+ }
19
+
20
+ let relativePath = path.replace(process.cwd(), '');
21
+ this._name = _path.basename(_path.resolve(path));
22
+ this._path = _path.join(relativePath, `.${_path.sep}`);
23
+
24
+ this._absolutePath = _path.join(_path.resolve(path), _path.sep);
25
+ this._globPath = this._absolutePath.replace(_sepRegexp, '/');
26
+
27
+ this._children = [];
28
+ }
29
+
30
+ /**
31
+ * Creates a folder tree based on an object that describes the tree
32
+ * structure.
33
+ *
34
+ * @param {String} rootPath A string that represents the path to the root
35
+ * directory of the tree.
36
+ * @param {Object} tree An object representation of the tree structure.
37
+ * @return {Directory} A directory object that represents the root of
38
+ * the tree.
39
+ */
40
+ static createTree(rootPath, tree) {
41
+ if (typeof rootPath !== 'string') {
42
+ throw new Error('Invalid rootPath specified (arg #1)');
43
+ }
44
+ if (!tree || tree instanceof Array || typeof tree !== 'object') {
45
+ throw new Error('Invalid tree specified (arg #2)');
46
+ }
47
+
48
+ function createRecursive(parent, tree) {
49
+ if (typeof parent === 'string') {
50
+ parent = new Directory(parent);
51
+ }
52
+
53
+ if (tree && !(tree instanceof Array) && typeof tree === 'object') {
54
+ for (let dirName in tree) {
55
+ let child = parent.addChild(dirName);
56
+ createRecursive(child, tree[dirName]);
57
+ }
58
+ }
59
+ return parent;
60
+ }
61
+
62
+ return createRecursive(rootPath, tree);
63
+ }
64
+
65
+ /**
66
+ * Traverses a directory tree, invoking the callback function at each level
67
+ * of the tree.
68
+ *
69
+ * @param {Directory} root The root level of the tree to traverse.
70
+ * @param {Function} callback The callback function that is invoked as each
71
+ * directory is traversed.
72
+ */
73
+ static traverseTree(root, callback) {
74
+ if (!(root instanceof Directory)) {
75
+ throw new Error('Invalid root directory specified (arg #1)');
76
+ }
77
+ if (typeof callback !== 'function') {
78
+ throw new Error('Invalid callback function specified (arg #1)');
79
+ }
80
+ function traverseRecursive(parent, level) {
81
+ callback(parent, level);
82
+ level++;
83
+ parent.getChildren().forEach((child) => {
84
+ traverseRecursive(child, level);
85
+ });
86
+ }
87
+
88
+ return traverseRecursive(root, 0);
89
+ }
90
+
91
+ /**
92
+ * Returns the name of the directory.
93
+ *
94
+ * @type {String}
95
+ */
96
+ get name() {
97
+ return this._name;
98
+ }
99
+
100
+ /**
101
+ * Returns the relative path to this directory.
102
+ *
103
+ * @return {String}
104
+ */
105
+ get path() {
106
+ return this._path;
107
+ }
108
+
109
+ /**
110
+ * Returns the absolute path to this directory.
111
+ *
112
+ * @type {String}
113
+ */
114
+ get absolutePath() {
115
+ return this._absolutePath;
116
+ }
117
+
118
+ /**
119
+ * Returns the glob path to the file.
120
+ */
121
+ get globPath() {
122
+ return this._globPath;
123
+ }
124
+
125
+ /**
126
+ * Adds a child directory object to the current directory.
127
+ *
128
+ * @param {String} name Name of the child directory.
129
+ */
130
+ addChild(name) {
131
+ if (typeof name !== 'string' || name.length <= 0) {
132
+ throw new Error('Invalid directory name specified (arg #1)');
133
+ }
134
+ if (name.match(/[\\/:]/)) {
135
+ throw new Error(
136
+ 'Directory name cannot include path separators (:, \\ or /)'
137
+ );
138
+ }
139
+ const child = new Directory(_path.join(this.path, name));
140
+ this._children.push(child);
141
+
142
+ return child;
143
+ }
144
+
145
+ /**
146
+ * Retrieves a child directory object by recursively searching through the
147
+ * current directory's child tree.
148
+ *
149
+ * @param {String} path The relative path to the child directory, with
150
+ * each path element separated by "/".
151
+ * @return {Directory} A directory reference for the specified child. If
152
+ * a child is not found at the specified path, an error will be
153
+ * thrown.
154
+ */
155
+ getChild(path) {
156
+ if (typeof path !== 'string' || path.length <= 0) {
157
+ throw new Error('Invalid child path specified (arg #1)');
158
+ }
159
+ const tokens = path.split('/');
160
+ const child = tokens.reduce((result, name) => {
161
+ if (!result) {
162
+ return result;
163
+ }
164
+ const children = result.getChildren();
165
+ return children.find((child) => child.name === name);
166
+ }, this);
167
+
168
+ if (!child) {
169
+ throw new Error(`Child not found at path: [${path}]`);
170
+ }
171
+ return child;
172
+ }
173
+
174
+ /**
175
+ * Returns an array containing all first level children of the current
176
+ * directory.
177
+ *
178
+ * @return {Directory[]} An array of first level children for the directory.
179
+ */
180
+ getChildren() {
181
+ return this._children.slice();
182
+ }
183
+
184
+ /**
185
+ * Returns the path to a file within the current directory. This file
186
+ * does not have to actually exist on the file system, and can also be the
187
+ * name of a directory.
188
+ *
189
+ * @param {String} fileName The name of the file.
190
+ * @return {String} The path to the file including the current directory
191
+ * path.
192
+ */
193
+ getFilePath(fileName) {
194
+ if (typeof fileName !== 'string') {
195
+ fileName = '';
196
+ }
197
+ return _path.join(this.absolutePath, fileName);
198
+ }
199
+
200
+ /**
201
+ * Returns a glob path to a file within the current directory. This file
202
+ * does not have to actually exist on the file system, and can also be the
203
+ * name of a directory.
204
+ *
205
+ * @param {String} fileName The name of the file.
206
+ * @return {String} The path to the file including the current directory
207
+ * path.
208
+ */
209
+ getFileGlob(fileName) {
210
+ if (typeof fileName !== 'string') {
211
+ fileName = '';
212
+ }
213
+ return _path.join(this.absolutePath, fileName).replace(_sepRegexp, '/');
214
+ }
215
+
216
+ /**
217
+ * Gets a string glob that can be used to match all folders/files in the
218
+ * current folder and all sub folders, optionally filtered by file
219
+ * extension.
220
+ *
221
+ * @param {String} [extension] An optional extension to use when generating
222
+ * a globbing pattern.
223
+ */
224
+ getAllFilesGlob(extension) {
225
+ if (typeof extension !== 'string') {
226
+ extension = '*';
227
+ } else {
228
+ extension = `*.${extension}`;
229
+ }
230
+ return `${this.globPath}**/${extension}`;
231
+ }
232
+ }
233
+
234
+ module.exports = Directory;
package/src/index.js ADDED
@@ -0,0 +1,22 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Utility library that can be used to create development tooling.
5
+ */
6
+ module.exports = {
7
+ /**
8
+ * A class that represents a directory on the file system.
9
+ */
10
+ Directory: require('./directory'),
11
+
12
+ /**
13
+ * Represents a specific project configuration.
14
+ */
15
+ Project: require('./project'),
16
+
17
+ /**
18
+ * A collection of task builder functions that can be used to generate
19
+ * commonly used gulp files for projects.
20
+ */
21
+ taskBuilders: require('./task-builders'),
22
+ };