autofront 2.2.2 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +55 -82
- package/index.js +500 -117
- package/package.json +41 -39
package/README.md
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
# Autofront
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Automation of front-end by [Gulp](https://gulpjs.com) and [Bower](https://bower.io).
|
|
4
4
|
|
|
5
5
|
## Get started
|
|
6
6
|
|
|
7
7
|
### Gulp
|
|
8
8
|
|
|
9
|
-
Install its CLI
|
|
9
|
+
Install its CLI (following [the official manual](https://gulpjs.com/docs/en/getting-started/quick-start/) but skipping [the local package](https://gulpjs.com/docs/en/getting-started/quick-start/#install-the-gulp-package-in-your-devdependencies) and the next steps).
|
|
10
10
|
|
|
11
11
|
And put `gulpfile.js` simply with:
|
|
12
12
|
|
|
@@ -18,113 +18,86 @@ require('autofront');
|
|
|
18
18
|
|
|
19
19
|
[Install it](https://bower.io/#install-bower), [initialize it and save dependencies](https://bower.io/#save-packages).
|
|
20
20
|
|
|
21
|
-
###
|
|
21
|
+
### Installation
|
|
22
22
|
|
|
23
23
|
```sh
|
|
24
24
|
npm install --save-dev autofront
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
-
###
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
```html
|
|
32
|
-
<!DOCTYPE html>
|
|
33
|
-
<html>
|
|
34
|
-
<head>
|
|
35
|
-
<!-- build:css styles/vendor.css -->
|
|
36
|
-
<!-- bower:css --><!-- endbower -->
|
|
37
|
-
<!-- endbuild -->
|
|
38
|
-
</head>
|
|
39
|
-
<body>
|
|
40
|
-
<!-- build:js scripts/vendor.js -->
|
|
41
|
-
<!-- bower:js --><!-- endbower -->
|
|
42
|
-
<!-- endbuild -->
|
|
43
|
-
<!-- build:js scripts/app.js -->
|
|
44
|
-
<!-- inject:js -->
|
|
45
|
-
<!-- endinject -->
|
|
46
|
-
<!-- endbuild -->
|
|
47
|
-
</body>
|
|
48
|
-
</html>
|
|
49
|
-
```
|
|
27
|
+
### Source code
|
|
28
|
+
|
|
29
|
+
Place inside directory `src`; at least including the main page (`index.html`), without embedding tags (`<link>`s and `<script>`s).
|
|
50
30
|
|
|
51
31
|
### Run
|
|
52
32
|
|
|
53
|
-
Finally initiate
|
|
33
|
+
Finally, initiate the project, commanding:
|
|
54
34
|
|
|
55
35
|
```sh
|
|
56
36
|
gulp
|
|
57
37
|
```
|
|
58
38
|
|
|
59
|
-
A browser tab
|
|
39
|
+
A browser tab is opened. Now you are ready to develop!
|
|
60
40
|
|
|
61
|
-
|
|
41
|
+
To reach further, see below.
|
|
62
42
|
|
|
63
43
|
## Usage
|
|
64
44
|
|
|
65
45
|
### Tasks
|
|
66
46
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
- `gulp` or `gulp serve` are for running a test server and develop with live reload.
|
|
70
|
-
- `gulp build` only builds production code, the distributable application (`dist` folder).
|
|
71
|
-
- With `gulp serve:dist`, a combination of the above is achieved: Specifically, the server runs that last version but without reload.
|
|
72
|
-
|
|
73
|
-
### Domains
|
|
74
|
-
|
|
75
|
-
On executing Gulp command, an additional parameter can be included (e.g.: `gulp --dev` or `gulp build --pro`) to indicate the domain of connection path. Defaults to `--local`.
|
|
76
|
-
|
|
77
|
-
These domain URLs would be searched in `package.json` listed in the property `domains` (optionally also `domainAliases`, to assign domain name for each alias).
|
|
78
|
-
|
|
79
|
-
And, to capture the selected URL string, put `{{AUTOFRONT_DOMAIN}}` where it would be located in your source code.
|
|
80
|
-
|
|
81
|
-
## Support
|
|
82
|
-
|
|
83
|
-
Positioning in source folder (`src`), you can utilize:
|
|
84
|
-
|
|
85
|
-
### HTML
|
|
86
|
-
|
|
87
|
-
Besides of required `index.html`, it is possible to add more files. In this case, they will be treated as [templates](https://docs.angularjs.org/api/ng/directive/ngInclude) and, to work properly with `dist`, AngularJS must to be [appropriately](#angularjs) on.
|
|
47
|
+
The Gulp ones are:
|
|
88
48
|
|
|
89
|
-
|
|
49
|
+
| Name | Details | Processes |
|
|
50
|
+
| --- | --- | --- |
|
|
51
|
+
| `serve` (default) | Source code runs in a server with live reload. | <ul><li>Bower entry-point files catching.</li><li>Notification and injection of [environment](#environment-variables).</li><li>Compilation (Less, SCSS and Pug)[^1].</li><li>Set up[^2] of HTML5 mode.[^1]</li><li>Insertion of file with app info (`about.json`).</li></ul> |
|
|
52
|
+
| `build` | Production code is built (in folder `dist`). | The above and: <ul><li>Templates caching.[^1]</li><li>Concatenation to one hashed file (CSS and JS).</li><li>Minification (HTML, CSS, JS, images and JSON).</li><li>Console display of files size.</li></ul> |
|
|
53
|
+
| `serve:dist` | This distributable application is served but without the refreshing. | The same with the folder hidden. |
|
|
90
54
|
|
|
91
|
-
|
|
55
|
+
[^1]: [If it is on](#settings).
|
|
56
|
+
[^2]: Invocation of [`$locationProvider`](https://docs.angularjs.org/api/ng/provider/$locationProvider#html5Mode) and a `<base>` injected.
|
|
92
57
|
|
|
93
|
-
###
|
|
58
|
+
### Environment variables
|
|
94
59
|
|
|
95
|
-
|
|
60
|
+
They can be used in this way:
|
|
96
61
|
|
|
97
|
-
|
|
62
|
+
1. Define them. Look at [the next section](#settings).
|
|
63
|
+
2. Put `AUTOFRONT_ENV` in your JS source code where it would be injected.
|
|
64
|
+
3. On executing Gulp command, indicate the name of the current one to the flag argument `env`. Defaults to "development" with server tasks and to "production" with `build`.
|
|
98
65
|
|
|
99
|
-
|
|
66
|
+
## Settings
|
|
100
67
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
Idem, but it is also obligatory an extra jointly: `_variables.scss`.
|
|
104
|
-
|
|
105
|
-
### JavaScript
|
|
106
|
-
|
|
107
|
-
Wherever you want.
|
|
108
|
-
|
|
109
|
-
#### [AngularJS](https://angularjs.org)
|
|
110
|
-
|
|
111
|
-
One of modules should to be named "app", ideally the main one.
|
|
112
|
-
|
|
113
|
-
Optionally, to work with [HTML5 mode](https://docs.angularjs.org/api/ng/provider/$locationProvider#html5Mode), invoke it:
|
|
68
|
+
You can configure it typing into Gulp file like this:
|
|
114
69
|
|
|
115
70
|
```js
|
|
116
|
-
|
|
117
|
-
|
|
71
|
+
const autofront = require('autofront');
|
|
72
|
+
|
|
73
|
+
autofront.property = {
|
|
74
|
+
subproperty: value,
|
|
75
|
+
subproperty2: {
|
|
76
|
+
subproperty3: value2,
|
|
77
|
+
// ...
|
|
78
|
+
},
|
|
79
|
+
// ...
|
|
80
|
+
};
|
|
81
|
+
autofront.property2.subproperty4 = value3;
|
|
82
|
+
// ...
|
|
118
83
|
```
|
|
119
84
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
85
|
+
Defining with:
|
|
86
|
+
|
|
87
|
+
| Property | Subprop. | | Type | Details | Default |
|
|
88
|
+
| --- | --- | --- | --- | --- | --- |
|
|
89
|
+
| `html` | `pug` | | Boolean | [Pug](https://pugjs.org) activated? | `false` |
|
|
90
|
+
| `css` | `folder` | | String | Directory that contains CSS files. | `'styles/'` |
|
|
91
|
+
| <!-- 〃 --> | `filename` | | String | Filename of root files. | `'index'` |
|
|
92
|
+
| <!-- 〃 --> | `order` | | Number | Index of order to include content in stylesheet. | `0` |
|
|
93
|
+
| <!-- 〃 --> | `less`[^3] | `order` | Number | Idem for [Less](https://lesscss.org). | `1` |
|
|
94
|
+
| <!-- 〃 --> | `scss`[^3] | `order` | Number | Idem for [SCSS (Sass)](https://sass-lang.com/documentation/syntax#scss). | `2` |
|
|
95
|
+
| <!-- 〃 --> | <!-- 〃 --> | `variables` | String | Filename of variables file. | `_variables` |
|
|
96
|
+
| <!-- 〃 --> | `fonts` | `folder` | String | Location (folder path) of font files from Bower. | `'fonts/'` |
|
|
97
|
+
| <!-- 〃 --> | <!-- 〃 --> | `extensions` | String or array of strings | File extensions to catch. | `['eot', 'otf', 'svg', 'ttf', 'woff', 'woff2']` |
|
|
98
|
+
| `js` | `ng`[^3] | `module` | String | Name of [AngularJS](https://angularjs.org) main module. | `'app'` |
|
|
99
|
+
| <!-- 〃 --> | <!-- 〃 --> | `html5Mode` | Boolean | [HTML5 mode](https://docs.angularjs.org/guide/$location#html5-mode) enabled? | `false` |
|
|
100
|
+
| <!-- 〃 --> | <!-- 〃 --> | `template` | Boolean | Templates loaded by [`$templateCache`](https://docs.angularjs.org/api/ng/service/$templateCache)? | `true` |
|
|
101
|
+
| <!-- 〃 --> | `envs` | | Object | Environment variables list, with names as keys and data (whatever can be JSON parsed) as values. | `{}` |
|
|
102
|
+
|
|
103
|
+
[^3]: It can be disabled assigning a falsy value.
|
package/index.js
CHANGED
|
@@ -1,153 +1,536 @@
|
|
|
1
|
-
|
|
1
|
+
const defSettings = {
|
|
2
|
+
html: { pug: false },
|
|
3
|
+
css: {
|
|
4
|
+
folder: 'styles/',
|
|
5
|
+
filename: 'index',
|
|
6
|
+
order: 0,
|
|
7
|
+
less: { order: 1 },
|
|
8
|
+
scss: {
|
|
9
|
+
order: 2,
|
|
10
|
+
variables: '_variables'
|
|
11
|
+
},
|
|
12
|
+
fonts: {
|
|
13
|
+
folder: 'fonts/',
|
|
14
|
+
extensions: ['eot', 'otf', 'svg', 'ttf', 'woff', 'woff2']
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
js: {
|
|
18
|
+
ng: {
|
|
19
|
+
module: 'app',
|
|
20
|
+
html5Mode: false,
|
|
21
|
+
template: true
|
|
22
|
+
},
|
|
23
|
+
envs: {}
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
const settings = this;
|
|
27
|
+
for (const name in defSettings)
|
|
28
|
+
settings[name] = { ...defSettings[name] };
|
|
2
29
|
|
|
3
30
|
const gulp = require('gulp'),
|
|
4
|
-
path = require('path'),
|
|
5
|
-
args = require('get-gulp-args')(),
|
|
6
|
-
mergeStream = require('merge-stream'),
|
|
7
|
-
mainBowerFiles = require('main-bower-files'),
|
|
8
31
|
$ = require('gulp-load-plugins')(),
|
|
9
|
-
injStr = $.injectString,
|
|
10
|
-
gulpSass = $.sass(require('sass')),
|
|
11
32
|
notifyError = $.notify.onError(error => error.message),
|
|
12
|
-
|
|
33
|
+
gulpFilter = ext => $.filter(getGlob(ext), { restore: true }),
|
|
34
|
+
browserSync = require('browser-sync').create(),
|
|
35
|
+
deleteEmpty = require('delete-empty'),
|
|
36
|
+
gulpHtmlmin = () => $.htmlmin({ collapseWhitespace: true, conservativeCollapse: true }),
|
|
37
|
+
fs = require('fs'),
|
|
38
|
+
hidefile = require('hidefile');
|
|
13
39
|
|
|
14
|
-
let
|
|
40
|
+
let defEnv = 'production',
|
|
41
|
+
envName,
|
|
42
|
+
envValue;
|
|
15
43
|
|
|
16
|
-
const allFiles =
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
44
|
+
const allFiles = getGlob(),
|
|
45
|
+
indexFile = 'index.html',
|
|
46
|
+
scriptsDir = 'scripts/',
|
|
47
|
+
jsFiles = getGlob('js'),
|
|
48
|
+
cssComment = '<!-- autofrontcss -->',
|
|
49
|
+
endCssComment = '<!-- endautofrontcss -->',
|
|
50
|
+
jsComment = '<!-- autofrontjs -->',
|
|
51
|
+
html5ModeJsFile = scriptsDir + 'html5-mode.js',
|
|
52
|
+
jsTemplatesFile = scriptsDir + 'templates.js',
|
|
53
|
+
endJsComment = '<!-- endautofrontjs -->',
|
|
54
|
+
cssFile = 'index.css',
|
|
55
|
+
jsFile = 'index.js';
|
|
56
|
+
let stylesDir,
|
|
57
|
+
stylesFilename,
|
|
58
|
+
stylesCssFile,
|
|
59
|
+
cssExtensions = [];
|
|
22
60
|
|
|
23
|
-
const
|
|
61
|
+
const globs = {
|
|
24
62
|
src: 'src/',
|
|
25
63
|
tmp: '.tmp/',
|
|
26
64
|
dist: 'dist/'
|
|
27
65
|
};
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
66
|
+
globs.hiddenDist = '.' + globs.dist;
|
|
67
|
+
globs.srcIndex = globs.src + indexFile;
|
|
68
|
+
globs.srcJs = globs.src + jsFiles;
|
|
69
|
+
globs.srcOthers = [globs.src + allFiles];
|
|
70
|
+
globs.tmpAllFiles = globs.tmp + allFiles;
|
|
71
|
+
globs.distIndexFile = globs.dist + indexFile;
|
|
72
|
+
globs.distTmpls = [globs.dist + getGlob('html'), '!' + globs.distIndexFile];
|
|
73
|
+
|
|
74
|
+
const nl = '\r\n',
|
|
36
75
|
tab = ' ';
|
|
37
76
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
77
|
+
function setDefaultEnv(cb) {
|
|
78
|
+
defEnv = 'development';
|
|
79
|
+
cb();
|
|
80
|
+
}
|
|
81
|
+
setDefaultEnv.displayName = 'set-default-env';
|
|
82
|
+
|
|
83
|
+
function setVariables(cb) {
|
|
84
|
+
stylesDir = getSetting('cssFolder');
|
|
85
|
+
stylesFilename = getSetting('filename');
|
|
86
|
+
stylesCssFile = stylesDir + cssFile;
|
|
87
|
+
|
|
88
|
+
for (const cssExt of [
|
|
89
|
+
{
|
|
90
|
+
name: 'css',
|
|
91
|
+
process: $.cssimport
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
name: 'less',
|
|
95
|
+
process: $.less,
|
|
96
|
+
isPreprocessor: true
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
name: 'scss',
|
|
100
|
+
process: $.sass(require('sass')),
|
|
101
|
+
getExtraCode: () => {
|
|
102
|
+
const file = globs.src + stylesDir + getSetting('variables') + '.scss';
|
|
103
|
+
return fileExists(file) ? `@import "${file}";` : '';
|
|
104
|
+
},
|
|
105
|
+
isPreprocessor: true
|
|
48
106
|
}
|
|
49
|
-
|
|
107
|
+
]) {
|
|
108
|
+
const name = cssExt.name;
|
|
109
|
+
if (!cssExt.isPreprocessor || getSetting(name))
|
|
110
|
+
cssExtensions.push({ getExtraCode: () => '', ...cssExt, order: getSetting(name + 'Order') });
|
|
50
111
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
gulp.task('index-domain', () => gulp.src(paths.tmp + getFiles('js')).pipe(injStr.replace('{{AUTOFRONT_DOMAIN}}', domain)).pipe(gulp.dest(paths.tmp)));
|
|
59
|
-
gulp.task('index', gulp.series('index-build', 'index-domain'));
|
|
60
|
-
gulp.task('styles', () => {
|
|
61
|
-
return mergeStream(getCssStream('css'), getCssStream('scss', gulpSass, '@import "variables";'), getCssStream('less', $.less))
|
|
62
|
-
.pipe($.concat(cssFullFilename))
|
|
63
|
-
.pipe(gulp.dest(paths.tmp + stylesFolder))
|
|
64
|
-
.pipe(browserSync.stream());
|
|
65
|
-
|
|
66
|
-
function getCssStream(ext, process, extraCode) {
|
|
67
|
-
let stream = gulp.src(paths.src + stylesFolder + cssFilename + '.' + ext, { allowEmpty: true });
|
|
68
|
-
if (process)
|
|
69
|
-
return stream.pipe(injStr.prepend((extraCode ? extraCode + nl : '') + '// bower:' + ext + nl + '// endbower' + nl)).pipe($.wiredep()).pipe(process()).on('error', notifyError);
|
|
70
|
-
return stream;
|
|
112
|
+
cssExtensions = cssExtensions.sort((a, b) => a.order - b.order);
|
|
113
|
+
|
|
114
|
+
const srcStyles = [];
|
|
115
|
+
for (const cssExt of cssExtensions) {
|
|
116
|
+
const glob = globs.src + getGlob(cssExt.name);
|
|
117
|
+
srcStyles.push(glob);
|
|
118
|
+
cssExt.glob = glob;
|
|
71
119
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
gulp.task('others', () => {
|
|
75
|
-
const pugFilter = filter('pug');
|
|
76
|
-
return gulp.src(paths.srcOthers)
|
|
77
|
-
.pipe(pugFilter).pipe($.pug()).on('error', notifyError).pipe(pugFilter.restore)
|
|
78
|
-
.pipe(gulp.dest(paths.tmp));
|
|
79
|
-
});
|
|
80
|
-
gulp.task('about', () => gulp.src('package.json').pipe($.about()).pipe(gulp.dest(paths.tmp)));
|
|
81
|
-
gulp.task('build:tmp', gulp.series('del', 'check', gulp.parallel('index', 'styles', 'fonts', 'others', 'about')));
|
|
82
|
-
gulp.task('browser', gulp.series('build:tmp', (cb) => {
|
|
83
|
-
browserSyncInit(paths.tmp);
|
|
120
|
+
globs.srcOthers.push(...[globs.srcIndex, globs.srcJs, ...srcStyles].map(glob => '!' + glob));
|
|
121
|
+
|
|
84
122
|
cb();
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
123
|
+
}
|
|
124
|
+
setVariables.displayName = 'set-variables';
|
|
125
|
+
|
|
126
|
+
function getEnv() {
|
|
127
|
+
envName = require('get-gulp-args')().env || defEnv;
|
|
128
|
+
envValue = getSetting('envs')[envName];
|
|
129
|
+
const isMatched = envValue !== undefined;
|
|
130
|
+
return gulp.src(globs.src, { read: false })
|
|
131
|
+
.pipe($.notify(isMatched ? `Matching environment: "${envName}".` : 'No environment matched.'));
|
|
132
|
+
}
|
|
133
|
+
getEnv.displayName = 'get-env';
|
|
134
|
+
|
|
135
|
+
function removeFolder() {
|
|
136
|
+
return delDir(globs.tmp);
|
|
137
|
+
}
|
|
138
|
+
removeFolder.displayName = 'remove-folder';
|
|
139
|
+
|
|
140
|
+
function createFolder() {
|
|
141
|
+
return gulp.src('*.*', { read: false })
|
|
142
|
+
.pipe(gulp.dest(globs.tmp));
|
|
143
|
+
}
|
|
144
|
+
createFolder.displayName = 'create-folder';
|
|
145
|
+
|
|
146
|
+
function hideFolder(cb) {
|
|
147
|
+
hideDir(globs.tmp);
|
|
148
|
+
cb();
|
|
149
|
+
}
|
|
150
|
+
hideFolder.displayName = 'hide-folder';
|
|
151
|
+
|
|
152
|
+
const addFolder = gulp.series(createFolder, hideFolder);
|
|
153
|
+
|
|
154
|
+
function index() {
|
|
155
|
+
const filename = 'vendor',
|
|
156
|
+
strs = [
|
|
157
|
+
cssComment,
|
|
158
|
+
`<!-- build:css ${stylesDir + filename}.css -->`,
|
|
159
|
+
'<!-- bower:css --><!-- endbower -->',
|
|
160
|
+
'<!-- endbuild -->'
|
|
161
|
+
];
|
|
162
|
+
for (const { name } of cssExtensions)
|
|
163
|
+
strs.push('<link rel="stylesheet" href="' + stylesDir + stylesFilename + (name != 'css' ? '.' + name : '') + '.css' + '">');
|
|
164
|
+
strs.push(
|
|
165
|
+
endCssComment,
|
|
166
|
+
jsComment,
|
|
167
|
+
`<!-- build:js ${scriptsDir + filename}.js defer -->`,
|
|
168
|
+
'<!-- bower:js --><!-- endbower -->',
|
|
169
|
+
'<!-- endbuild -->',
|
|
170
|
+
'<!-- inject:js -->',
|
|
171
|
+
'<!-- endinject -->'
|
|
172
|
+
);
|
|
173
|
+
if (getSetting('html5Mode')) {
|
|
174
|
+
strs.unshift('<base href="/">');
|
|
175
|
+
strs.push(getScriptTag(html5ModeJsFile));
|
|
176
|
+
}
|
|
177
|
+
strs.push(endJsComment);
|
|
178
|
+
|
|
179
|
+
let stream = gulp.src(globs.srcJs);
|
|
180
|
+
if (getSetting('ng'))
|
|
181
|
+
stream = stream.pipe($.angularFilesort());
|
|
182
|
+
|
|
183
|
+
return gulp.src(globs.srcIndex)
|
|
184
|
+
.pipe($.injectString.before('</head>', tab + strs.join(nl + tab) + nl))
|
|
185
|
+
.pipe($.inject(stream, { relative: true, transform: filepath => getScriptTag(filepath) })).on('error', notifyError)
|
|
186
|
+
.pipe($.wiredep())
|
|
187
|
+
.pipe($.useref())
|
|
188
|
+
.pipe(gulp.dest(globs.tmp));
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function js() {
|
|
192
|
+
return gulp.src(globs.srcJs)
|
|
193
|
+
.pipe(replace('AUTOFRONT_ENV', JSON.stringify(envValue, undefined, tab)))
|
|
194
|
+
.pipe(gulp.dest(globs.tmp));
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const indexAndJs = gulp.parallel(index, js);
|
|
198
|
+
|
|
199
|
+
const css = getStylesTask('css');
|
|
200
|
+
|
|
201
|
+
const less = getStylesTask('less');
|
|
202
|
+
|
|
203
|
+
const scss = getStylesTask('scss');
|
|
204
|
+
|
|
205
|
+
const styles = gulp.parallel(css, less, scss);
|
|
206
|
+
|
|
207
|
+
function html5Mode(cb) {
|
|
208
|
+
if (getSetting('html5Mode'))
|
|
209
|
+
return $.addFiles([{
|
|
210
|
+
name: html5ModeJsFile,
|
|
211
|
+
content: `(function () {
|
|
212
|
+
angular.module('${getSetting('module')}')
|
|
213
|
+
.config(config);
|
|
214
|
+
|
|
215
|
+
function config($locationProvider) {
|
|
216
|
+
$locationProvider.html5Mode(true);
|
|
217
|
+
}
|
|
218
|
+
})();`
|
|
219
|
+
}])
|
|
220
|
+
.pipe(gulp.dest(globs.tmp));
|
|
221
|
+
|
|
222
|
+
cb();
|
|
223
|
+
}
|
|
224
|
+
html5Mode.displayName = 'html5-mode';
|
|
225
|
+
|
|
226
|
+
function fonts(cb) {
|
|
227
|
+
const glob = require('main-bower-files')(getGlob(getSetting('extensions')));
|
|
228
|
+
if (glob.length)
|
|
229
|
+
return gulp.src(glob)
|
|
230
|
+
.pipe(gulp.dest(globs.tmp + getSetting('fontsFolder')));
|
|
231
|
+
|
|
232
|
+
cb();
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function others() {
|
|
236
|
+
let stream = gulp.src(globs.srcOthers);
|
|
237
|
+
if (getSetting('pug')) {
|
|
238
|
+
const pugFilter = gulpFilter('pug');
|
|
239
|
+
stream = stream.pipe(pugFilter).pipe($.pug()).on('error', notifyError).pipe(pugFilter.restore);
|
|
240
|
+
}
|
|
241
|
+
return stream.pipe(gulp.dest(globs.tmp));
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
function about() {
|
|
245
|
+
return gulp.src('package.json')
|
|
246
|
+
.pipe($.about({ inject: { environment: envName } }))
|
|
247
|
+
.pipe(gulp.dest(globs.tmp));
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const buildTmp = gulp.series(
|
|
251
|
+
gulp.parallel(getEnv, removeFolder),
|
|
252
|
+
addFolder,
|
|
253
|
+
gulp.parallel(indexAndJs, styles, html5Mode, fonts, others, about)
|
|
254
|
+
);
|
|
255
|
+
|
|
256
|
+
function browser(cb) {
|
|
257
|
+
browserSyncInit(globs.tmp);
|
|
258
|
+
cb();
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
function reload(cb) {
|
|
262
|
+
browserSync.reload();
|
|
263
|
+
cb();
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
function watch() {
|
|
267
|
+
gulp.watch(globs.srcIndex, index);
|
|
268
|
+
gulp.watch(globs.srcJs)
|
|
269
|
+
.on('add', indexAndJs)
|
|
270
|
+
.on('change', gulp.series(js))
|
|
271
|
+
.on('unlink', gulp.series(index))
|
|
272
|
+
.on('unlink', path => { delFile(path); });
|
|
273
|
+
|
|
274
|
+
for (cssExt of cssExtensions)
|
|
275
|
+
gulp.watch(cssExt.glob, eval(cssExt.name));
|
|
276
|
+
|
|
277
|
+
gulp.watch(globs.srcOthers)
|
|
278
|
+
.on('add', gulp.series(others))
|
|
279
|
+
.on('change', gulp.series(others))
|
|
280
|
+
.on('unlink', path => { delFile(path, replaceExt); });
|
|
281
|
+
|
|
282
|
+
gulp.watch([globs.tmpAllFiles, '!' + globs.tmp + stylesDir + getGlob('css')], reload).on('unlink', () => { deleteEmpty(globs.tmp); });
|
|
283
|
+
|
|
284
|
+
function delFile(path, fn) {
|
|
285
|
+
path = path.replaceAll('\\', '/').replace(globs.src, globs.tmp);
|
|
286
|
+
if (fn)
|
|
287
|
+
path = fn(path);
|
|
288
|
+
gulp.src(path, { read: false })
|
|
289
|
+
.pipe($.clean());
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function replaceExt(path) {
|
|
293
|
+
if (getSetting('pug'))
|
|
294
|
+
return path.replace('.pug', '.html');
|
|
295
|
+
return path;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
gulp.task('serve', gulp.series(
|
|
300
|
+
gulp.parallel(setDefaultEnv, setVariables),
|
|
301
|
+
buildTmp, browser, watch
|
|
302
|
+
));
|
|
303
|
+
|
|
304
|
+
function removeFolderDist() {
|
|
305
|
+
return delDir([globs.dist, globs.hiddenDist]);
|
|
306
|
+
}
|
|
307
|
+
removeFolderDist.displayName = 'remove-folder:dist';
|
|
308
|
+
|
|
309
|
+
function copy() {
|
|
310
|
+
return gulp.src(globs.tmpAllFiles)
|
|
311
|
+
.pipe($.size())
|
|
312
|
+
.pipe(gulp.dest(globs.dist));
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function templates() {
|
|
316
|
+
let stream = gulp.src(globs.distTmpls)
|
|
317
|
+
.pipe(gulpHtmlmin());
|
|
318
|
+
if (getSetting('template'))
|
|
319
|
+
stream = stream.pipe($.angularTemplatecache(jsTemplatesFile, { module: getSetting('module'), transformUrl: url => url.slice(1) }));
|
|
320
|
+
return stream.pipe(gulp.dest(globs.dist));
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function indexDist() {
|
|
324
|
+
const replaces = Object.entries({
|
|
325
|
+
[cssComment]: `<!-- build:css ${stylesCssFile} -->`,
|
|
326
|
+
[endCssComment]: '<!-- endbuild -->',
|
|
327
|
+
[jsComment]: `<!-- build:js ${jsFile} defer -->`,
|
|
328
|
+
[endJsComment]: '<!-- endbuild -->'
|
|
93
329
|
});
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
330
|
+
let stream = gulp.src(globs.distIndexFile);
|
|
331
|
+
if (getSetting('template') && fileExists(globs.dist + jsTemplatesFile))
|
|
332
|
+
stream = stream.pipe($.injectString.before(endJsComment, getScriptTag(jsTemplatesFile) + nl + tab));
|
|
333
|
+
for (const [search, str] of replaces)
|
|
334
|
+
stream = stream.pipe($.injectString.replace(search, str));
|
|
335
|
+
return stream
|
|
336
|
+
.pipe($.useref())
|
|
337
|
+
.pipe(gulp.dest(globs.dist));
|
|
338
|
+
}
|
|
339
|
+
indexDist.displayName = 'index:dist';
|
|
340
|
+
|
|
341
|
+
function rebaseCss() {
|
|
342
|
+
return gulp.src(globs.dist + stylesCssFile)
|
|
343
|
+
.pipe($.rebaseCssUrls(globs.dist))
|
|
344
|
+
.pipe(gulp.dest(globs.dist));
|
|
345
|
+
}
|
|
346
|
+
rebaseCss.displayName = 'rebase-css';
|
|
347
|
+
|
|
348
|
+
function rebaseHtml() {
|
|
349
|
+
return gulp.src(globs.distIndexFile)
|
|
350
|
+
.pipe($.injectString.replace(` href="${stylesCssFile}"`, ` href="${cssFile}"`))
|
|
351
|
+
.pipe(gulp.dest(globs.dist));
|
|
352
|
+
}
|
|
353
|
+
rebaseHtml.displayName = 'rebase-html';
|
|
354
|
+
|
|
355
|
+
const rebase = gulp.parallel(rebaseCss, rebaseHtml);
|
|
356
|
+
|
|
357
|
+
function cleanFiles() {
|
|
358
|
+
return gulp.src([
|
|
359
|
+
...(getSetting('template') ? globs.distTmpls : []),
|
|
360
|
+
globs.dist + getGlob('css'), '!' + globs.dist + cssFile,
|
|
361
|
+
globs.dist + jsFiles, '!' + globs.dist + jsFile
|
|
362
|
+
], { read: false })
|
|
363
|
+
.pipe($.clean());
|
|
364
|
+
}
|
|
365
|
+
cleanFiles.displayName = 'clean-files';
|
|
366
|
+
|
|
367
|
+
function cleanFolders() {
|
|
368
|
+
return deleteEmpty(globs.dist);
|
|
369
|
+
}
|
|
370
|
+
cleanFolders.displayName = 'clean-folders';
|
|
371
|
+
|
|
372
|
+
const clean = gulp.series(cleanFiles, cleanFolders);
|
|
373
|
+
|
|
374
|
+
const minify = gulp.parallel(
|
|
375
|
+
getMinifyTask('html', stream => stream.pipe(gulpHtmlmin())),
|
|
376
|
+
getMinifyTask('css', stream => stream.pipe($.postcss([require('cssnano')()]))),
|
|
377
|
+
getMinifyTask('js', stream => {
|
|
378
|
+
if (getSetting('ng'))
|
|
379
|
+
stream = stream.pipe($.ngAnnotate());
|
|
380
|
+
return stream.pipe($.terser());
|
|
381
|
+
}),
|
|
382
|
+
getMinifyTask(['png', 'jpg', 'gif', 'svg'], stream => stream.pipe($.imagemin()), 'img'),
|
|
383
|
+
getMinifyTask('json', stream => stream.pipe($.jsonmin()))
|
|
384
|
+
);
|
|
385
|
+
|
|
386
|
+
function finishBuild() {
|
|
387
|
+
const cssAndJsFilter = gulpFilter(['css', 'js']);
|
|
388
|
+
return gulp.src(globs.dist + allFiles)
|
|
112
389
|
.pipe(cssAndJsFilter).pipe($.rev()).pipe($.revDeleteOriginal()).pipe(cssAndJsFilter.restore)
|
|
113
390
|
.pipe($.revReplace())
|
|
114
|
-
.pipe(imgFilter).pipe($.imagemin()).pipe(imgFilter.restore)
|
|
115
|
-
.pipe(jsonFilter).pipe($.jsonmin()).pipe(jsonFilter.restore)
|
|
116
391
|
.pipe($.size({ showFiles: true }))
|
|
117
|
-
.pipe(gulp.dest(
|
|
118
|
-
}
|
|
119
|
-
|
|
392
|
+
.pipe(gulp.dest(globs.dist));
|
|
393
|
+
}
|
|
394
|
+
finishBuild.displayName = 'finish-build';
|
|
395
|
+
|
|
396
|
+
gulp.task('build', gulp.series(
|
|
397
|
+
setVariables,
|
|
398
|
+
gulp.parallel(buildTmp, removeFolderDist),
|
|
399
|
+
copy, templates, indexDist, rebase, clean, minify, finishBuild
|
|
400
|
+
));
|
|
401
|
+
|
|
402
|
+
function hideFolderDist(cb) {
|
|
403
|
+
hideDir(globs.dist);
|
|
404
|
+
cb();
|
|
405
|
+
}
|
|
406
|
+
hideFolderDist.displayName = 'hide-folder:dist';
|
|
407
|
+
|
|
408
|
+
function browserDist(cb) {
|
|
409
|
+
browserSyncInit(globs.hiddenDist);
|
|
410
|
+
cb();
|
|
411
|
+
}
|
|
412
|
+
browserDist.displayName = 'browser:dist';
|
|
413
|
+
|
|
414
|
+
gulp.task('serve:dist', gulp.series(setDefaultEnv, 'build', hideFolderDist, browserDist));
|
|
415
|
+
|
|
416
|
+
gulp.task('default', gulp.series('serve'));
|
|
417
|
+
|
|
418
|
+
function getGlob(ext = '*') {
|
|
419
|
+
const glob = '**/*.';
|
|
420
|
+
if (typeof ext == 'string')
|
|
421
|
+
return glob + ext;
|
|
422
|
+
return glob + '{' + ext.join() + ',}';
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
function getSetting(name) {
|
|
426
|
+
switch (name) {
|
|
427
|
+
case 'pug':
|
|
428
|
+
return getValue('html');
|
|
429
|
+
case 'cssFolder':
|
|
430
|
+
case 'filename':
|
|
431
|
+
case 'cssOrder':
|
|
432
|
+
return getValue('css');
|
|
433
|
+
case 'less':
|
|
434
|
+
case 'scss':
|
|
435
|
+
return getValue('css', true);
|
|
436
|
+
case 'lessOrder':
|
|
437
|
+
return getValue('css.less', true, true);
|
|
438
|
+
case 'scssOrder':
|
|
439
|
+
case 'variables':
|
|
440
|
+
return getValue('css.scss', true, true);
|
|
441
|
+
case 'fontsFolder':
|
|
442
|
+
case 'extensions':
|
|
443
|
+
return getValue('css.fonts');
|
|
444
|
+
case 'ng':
|
|
445
|
+
return getValue('js', true);
|
|
446
|
+
case 'module':
|
|
447
|
+
case 'html5Mode':
|
|
448
|
+
case 'template':
|
|
449
|
+
return getValue('js.ng', true, true);
|
|
450
|
+
case 'envs':
|
|
451
|
+
return getValue('js');
|
|
452
|
+
}
|
|
120
453
|
|
|
121
|
-
|
|
454
|
+
function getValue(str, withoutDefault, onlyIfFalsy) {
|
|
455
|
+
for (const str of ['Folder', 'Order'])
|
|
456
|
+
if (name.endsWith(str))
|
|
457
|
+
name = str.toLowerCase();
|
|
122
458
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
459
|
+
const nameStr = str + '.' + name,
|
|
460
|
+
value = eval('settings.' + nameStr.replaceAll('.', '?.'));
|
|
461
|
+
if (withoutDefault) {
|
|
462
|
+
if (!onlyIfFalsy || !getSetting(str.split('.').pop()))
|
|
463
|
+
return value;
|
|
464
|
+
}
|
|
465
|
+
return value ?? eval('defSettings.' + nameStr);
|
|
466
|
+
}
|
|
127
467
|
}
|
|
128
468
|
|
|
129
|
-
function
|
|
130
|
-
|
|
131
|
-
return '**/*.' + (isArray ? '{' : '') + (isArray ? ext.join() : ext) + (isArray ? '}' : '');
|
|
469
|
+
function fileExists(path) {
|
|
470
|
+
return fs.existsSync(path);
|
|
132
471
|
}
|
|
133
472
|
|
|
134
|
-
function
|
|
135
|
-
return gulp.src(
|
|
473
|
+
function delDir(glob) {
|
|
474
|
+
return gulp.src(glob, { allowEmpty: true, read: false })
|
|
136
475
|
.pipe($.clean());
|
|
137
476
|
}
|
|
138
477
|
|
|
139
|
-
function
|
|
140
|
-
|
|
478
|
+
function hideDir(glob) {
|
|
479
|
+
hidefile.hideSync(glob, error => notifyError(error));
|
|
141
480
|
}
|
|
142
481
|
|
|
143
|
-
function
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
482
|
+
function getScriptTag(src) {
|
|
483
|
+
return `<script src="${src}" defer></script>`;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
function replace(search, str) {
|
|
487
|
+
for (const char of ['$', '.', '/', '('])
|
|
488
|
+
search = search.replaceAll(char, '\\' + char);
|
|
489
|
+
return $.injectString.replace(search, str);
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
function getStylesTask(ext) {
|
|
493
|
+
const fn = cb => {
|
|
494
|
+
const cssExt = cssExtensions.find(({ name }) => name == ext);
|
|
495
|
+
if (cssExt) {
|
|
496
|
+
const stylesFile = stylesFilename + '.' + ext,
|
|
497
|
+
srcStylesFile = globs.src + stylesDir + stylesFile,
|
|
498
|
+
isPreprocessor = cssExt.isPreprocessor,
|
|
499
|
+
extraCode = cssExt.getExtraCode(),
|
|
500
|
+
sep = nl + nl,
|
|
501
|
+
content = (extraCode ? extraCode + sep : '') + (isPreprocessor ? '// bower:' + ext + nl + '// endbower' : '');
|
|
502
|
+
let stream;
|
|
503
|
+
if (!fileExists(srcStylesFile))
|
|
504
|
+
stream = $.addFiles([{
|
|
505
|
+
name: stylesFile,
|
|
506
|
+
content
|
|
507
|
+
}]);
|
|
508
|
+
else
|
|
509
|
+
stream = gulp.src(srcStylesFile)
|
|
510
|
+
.pipe($.injectString.prepend(content ? content + sep : ''));
|
|
511
|
+
if (isPreprocessor)
|
|
512
|
+
stream = stream.pipe($.wiredep());
|
|
513
|
+
stream = stream.pipe(cssExt.process()).on('error', notifyError);
|
|
514
|
+
if (isPreprocessor)
|
|
515
|
+
stream = stream.pipe($.rename({ suffix: '.' + ext }));
|
|
516
|
+
return stream
|
|
517
|
+
.pipe(gulp.dest(globs.tmp + stylesDir))
|
|
518
|
+
.pipe(browserSync.stream());
|
|
147
519
|
}
|
|
148
|
-
|
|
520
|
+
|
|
521
|
+
cb();
|
|
522
|
+
};
|
|
523
|
+
fn.displayName = ext;
|
|
524
|
+
return fn;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
function browserSyncInit(path) {
|
|
528
|
+
browserSync.init({ server: path });
|
|
149
529
|
}
|
|
150
530
|
|
|
151
|
-
function
|
|
152
|
-
|
|
531
|
+
function getMinifyTask(ext, getProcessedStream, str) {
|
|
532
|
+
const fn = () => getProcessedStream(gulp.src(globs.dist + getGlob(ext)))
|
|
533
|
+
.pipe(gulp.dest(globs.dist));
|
|
534
|
+
fn.displayName = 'minify-' + (str || ext);
|
|
535
|
+
return fn;
|
|
153
536
|
}
|
package/package.json
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "autofront",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "3.0.0",
|
|
4
|
+
"description": "Automation of front-end by Gulp and Bower.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"serve": "gulp",
|
|
8
|
-
"serve:dist": "gulp serve:dist",
|
|
9
|
-
"build": "gulp build"
|
|
10
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
|
8
|
+
"serve:dist": "gulp serve:dist --env=preproduction",
|
|
9
|
+
"build": "gulp build"
|
|
11
10
|
},
|
|
12
11
|
"repository": {
|
|
13
12
|
"type": "git",
|
|
@@ -18,7 +17,7 @@
|
|
|
18
17
|
"pug",
|
|
19
18
|
"css",
|
|
20
19
|
"less",
|
|
21
|
-
"
|
|
20
|
+
"scss",
|
|
22
21
|
"javascript",
|
|
23
22
|
"angularjs"
|
|
24
23
|
],
|
|
@@ -29,38 +28,41 @@
|
|
|
29
28
|
},
|
|
30
29
|
"homepage": "https://github.com/mvicens/autofront#readme",
|
|
31
30
|
"dependencies": {
|
|
32
|
-
"browser-sync": "
|
|
33
|
-
"
|
|
34
|
-
"
|
|
35
|
-
"gulp": "
|
|
36
|
-
"gulp
|
|
37
|
-
"gulp-
|
|
38
|
-
"gulp-
|
|
39
|
-
"gulp-
|
|
40
|
-
"gulp-
|
|
41
|
-
"gulp-
|
|
42
|
-
"gulp-
|
|
43
|
-
"gulp-filter": "
|
|
44
|
-
"gulp-htmlmin": "
|
|
45
|
-
"gulp-imagemin": "
|
|
46
|
-
"gulp-inject": "
|
|
47
|
-
"gulp-inject-string": "
|
|
48
|
-
"gulp-jsonmin": "
|
|
49
|
-
"gulp-less": "
|
|
50
|
-
"gulp-load-plugins": "
|
|
51
|
-
"gulp-ng-annotate": "
|
|
52
|
-
"gulp-notify": "
|
|
53
|
-
"gulp-
|
|
54
|
-
"gulp-
|
|
55
|
-
"gulp-
|
|
56
|
-
"gulp-
|
|
57
|
-
"gulp-
|
|
58
|
-
"gulp-
|
|
59
|
-
"gulp-
|
|
60
|
-
"gulp-
|
|
61
|
-
"gulp-
|
|
62
|
-
"
|
|
63
|
-
"
|
|
64
|
-
"
|
|
31
|
+
"browser-sync": "2.27.10",
|
|
32
|
+
"cssnano": "5.1.14",
|
|
33
|
+
"delete-empty": "2.0.0",
|
|
34
|
+
"get-gulp-args": "0.0.1",
|
|
35
|
+
"gulp": "4.0.2",
|
|
36
|
+
"gulp-about": "1.1.0",
|
|
37
|
+
"gulp-add-files": "1.0.0",
|
|
38
|
+
"gulp-angular-filesort": "1.2.1",
|
|
39
|
+
"gulp-angular-templatecache": "3.0.1",
|
|
40
|
+
"gulp-clean": "0.4.0",
|
|
41
|
+
"gulp-cssimport": "7.0.0",
|
|
42
|
+
"gulp-filter": "7.0.0",
|
|
43
|
+
"gulp-htmlmin": "5.0.1",
|
|
44
|
+
"gulp-imagemin": "7.1.0",
|
|
45
|
+
"gulp-inject": "5.0.5",
|
|
46
|
+
"gulp-inject-string": "1.1.2",
|
|
47
|
+
"gulp-jsonmin": "1.2.0",
|
|
48
|
+
"gulp-less": "5.0.0",
|
|
49
|
+
"gulp-load-plugins": "2.0.8",
|
|
50
|
+
"gulp-ng-annotate": "2.1.0",
|
|
51
|
+
"gulp-notify": "4.0.0",
|
|
52
|
+
"gulp-postcss": "9.0.1",
|
|
53
|
+
"gulp-pug": "5.0.0",
|
|
54
|
+
"gulp-rebase-css-urls": "0.0.2",
|
|
55
|
+
"gulp-rename": "2.0.0",
|
|
56
|
+
"gulp-rev": "9.0.0",
|
|
57
|
+
"gulp-rev-delete-original": "0.2.3",
|
|
58
|
+
"gulp-rev-replace": "0.4.4",
|
|
59
|
+
"gulp-sass": "5.1.0",
|
|
60
|
+
"gulp-size": "4.0.1",
|
|
61
|
+
"gulp-terser": "2.1.0",
|
|
62
|
+
"gulp-useref": "5.0.0",
|
|
63
|
+
"gulp-wiredep": "1.2.0",
|
|
64
|
+
"hidefile": "3.0.0",
|
|
65
|
+
"main-bower-files": "2.13.3",
|
|
66
|
+
"sass": "1.56.1"
|
|
65
67
|
}
|
|
66
68
|
}
|