@videinfra/static-website-builder 1.13.4 → 1.15.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/CHANGELOG.md CHANGED
@@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.
4
4
  The format is based on [Keep a Changelog](http://keepachangelog.com/)
5
5
  and this project adheres to [Semantic Versioning](http://semver.org/).
6
6
 
7
+ ## [1.15.0] - 2025-01-20
8
+ ### Added
9
+ - Env file support
10
+
11
+ ## [1.14.0] - 2025-01-16
12
+ ### Added
13
+ - Fail build process when TWIG task fails
14
+
7
15
  ## [1.13.4] - 2024-10-08
8
16
  ### Fixed
9
17
  - Fail gulp process when htmlmin task fails
package/init/test/.env ADDED
@@ -0,0 +1,2 @@
1
+ HOST=https://test-global.tld
2
+ FOO=foo-global
@@ -0,0 +1,2 @@
1
+ HOST=https://test-local.tld
2
+ BAR=bar-local
@@ -33,6 +33,13 @@ exports.plugins = [
33
33
  require('../../../plugins/twig'),
34
34
  ];
35
35
 
36
+ exports.env = {
37
+ map: {
38
+ 'HOST': 'host',
39
+ 'FOO': 'foo',
40
+ 'BAR': 'bar',
41
+ },
42
+ };
36
43
 
37
44
  /*
38
45
  * Path configuration
@@ -45,4 +52,5 @@ exports.plugins = [
45
52
  exports.paths = {
46
53
  src: './init/test/src',
47
54
  dest: './tests/build/public',
55
+ env: ['./init/test/.env', './init/test/.env.local'],
48
56
  };
@@ -0,0 +1,6 @@
1
+ {% from 'macros/icon.twig' import icon %}
2
+ {% extends 'layouts/base.twig' %}
3
+
4
+ {% block body %}
5
+ <p>HOST: {{ host }}</p>
6
+ {% endblock %}
@@ -1 +1,5 @@
1
+ /* global process */
1
2
  console.log('Hello from main page!');
3
+ console.log('env.host ==', process.env.host);
4
+ console.log('env.foo ==', process.env.foo);
5
+ console.log('env.bar ==', process.env.bar);
@@ -0,0 +1,3 @@
1
+ .env-test:before {
2
+ content: $env-host;
3
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@videinfra/static-website-builder",
3
- "version": "1.13.4",
3
+ "version": "1.15.0",
4
4
  "description": "Customizable static site project builder",
5
5
  "license": "MIT",
6
6
  "engines": {
@@ -36,6 +36,7 @@
36
36
  "cross-env": "^7.0.3",
37
37
  "cssnano": "^6.0.1",
38
38
  "del": "^6.0.0",
39
+ "dotenv": "^16.4.7",
39
40
  "gulp": "^4.0.2",
40
41
  "gulp-cached": "^1.1.1",
41
42
  "gulp-data": "^1.3.1",
@@ -1,8 +1,10 @@
1
1
  const paths = require('./../../lib/get-path');
2
2
  const getConfig = require('./../../lib/get-config');
3
3
  const getPaths = require('./../../lib/get-path');
4
+ const merge = require('./../../lib/merge');
5
+ const getEnvData = require('./../../tasks/env/get-env');
4
6
  const assign = require('lodash/assign');
5
- const gulpSass = require('gulp-sass');
7
+ const gulpSass = require('../../vendor/gulp-sass/index');
6
8
 
7
9
 
8
10
  /**
@@ -33,7 +35,10 @@ module.exports = function processSASSConfig (config, fullConfig) {
33
35
  // Engine is a function which returns a gulp pipe function
34
36
  config.engine = function getSASSEngine () {
35
37
  const sass = config.legacy ? gulpSass(require('node-sass')) : gulpSass(require('sass'));
36
- return sass(getConfig.getTaskConfig('stylesheets', 'sass')).on('error', sass.logError)
38
+ const sassConfig = getConfig.getTaskConfig('stylesheets', 'sass');
39
+
40
+ sassConfig.data = merge(getEnvData().sass, sassConfig.data || {});
41
+ return sass(sassConfig).on('error', sass.logError)
37
42
  };
38
43
 
39
44
  // Main 'dependents' config is shared between all tasks
@@ -27,6 +27,12 @@ module.exports = function preprocessHTMLConfig (config = {}, fullConfig) {
27
27
  if (config.twig.filters) {
28
28
  config.twig.filters = flattenDeep(config.twig.filters);
29
29
  }
30
+ if (Array.isArray(config.twig.extend)) {
31
+ const extendFnList = config.twig.extend;
32
+ config.twig.extend = function (Twig) {
33
+ extendFnList.forEach((fn) => fn(Twig));
34
+ };
35
+ }
30
36
 
31
37
  // Main 'dependents' config is shared between all tasks
32
38
  if (config.dependents) {
package/plugins/twig.js CHANGED
@@ -33,6 +33,9 @@ exports.html = {
33
33
  // Disabled cache by default, it's enabled for production build only
34
34
  cache: false,
35
35
 
36
+ // Rethrow TWIG error so that process exists
37
+ rethrow: true,
38
+
36
39
  // Custom functions
37
40
  functions: [
38
41
  // require('../../plugins/twig/symfony-functions.js'),
@@ -8,7 +8,7 @@ const getConfig = require('../../lib/get-config');
8
8
  const logError = require('../../lib/log-error');
9
9
  const getFileNamesSync = require('../../lib/get-file-names');
10
10
  const camelizeFileName = require('../../lib/camelize-file-name');
11
-
11
+ const getEnvData = require('../env/get-env');
12
12
 
13
13
  function getData () {
14
14
  const folders = getPaths.getSourcePaths('data');
@@ -61,7 +61,8 @@ function getData () {
61
61
  return data;
62
62
  }, {});
63
63
 
64
- return data;
64
+ // Merge with env variables
65
+ return merge(data, getEnvData().twig);
65
66
  }
66
67
 
67
68
 
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Enviromental variable loading configuration
3
+ * Environment variables are loaded from .env files and remapped so that they can
4
+ * be used in TWIG, SASS and JavaScript:
5
+ * in TWIG `host` is accessible as `host`
6
+ * in SCSS `host` is accessible as `map-get($env, host)`
7
+ * in JS `host` is accessible as `process.env.host`
8
+ */
9
+
10
+ exports.env = {
11
+ // How env variable names should be remapped
12
+ // Example:
13
+ // map: { 'HOST': 'host', 'RECAPTCHA3_PUBLIC_KEY': 'recaptcha3_site_key' }
14
+ map: {}
15
+ };
16
+
17
+ exports.paths = {
18
+ // Env files which to load relative to project folder
19
+ env: [
20
+ '../.env',
21
+ '../.env.local',
22
+ ],
23
+ };
@@ -0,0 +1,51 @@
1
+ const paths = require('../../lib/get-path');
2
+ const dotenv = require('dotenv');
3
+ const getConfig = require('../../lib/get-config');
4
+
5
+ function escapeJSVariable (value) {
6
+ if (value === true || value === false || !isNaN(value)) {
7
+ return value;
8
+ } else {
9
+ // Convert to string
10
+ return "'" + value.replace(/\\/g, '\\\\').replace(/'/g, '\\\'').replace(/\n/g, '\\n') + "'";
11
+ }
12
+ }
13
+
14
+ function getEnvData () {
15
+ const envVariables = {};
16
+ const twigVariables = {};
17
+ const scssVariables = {};
18
+ const jsVariables = {};
19
+
20
+ const envFiles = paths.getPathConfig().env.map((path) => paths.getProjectPath(path));
21
+
22
+
23
+ dotenv.config({
24
+ // dotenv file order is reversed, values in first file overwrite all other
25
+ // file values
26
+ path: envFiles.reverse(),
27
+ processEnv: envVariables
28
+ });
29
+
30
+ // Remap property names
31
+ const map = getConfig.getTaskConfig('env', 'map');
32
+
33
+ Object.keys(map).forEach(key => {
34
+ if (key in envVariables) {
35
+ const value = envVariables[key];
36
+ const camelCase = map[key];
37
+ const kebabCase = map[key].replace(/([a-z])([A-Z])/g, '$1-$2').replace(/_([a-z])/ig, '-$1').toLowerCase();
38
+ twigVariables[camelCase] = value;
39
+ jsVariables[`process.env.${ camelCase }`] = escapeJSVariable(value);
40
+ scssVariables[`env-${ kebabCase }`] = value;
41
+ }
42
+ });
43
+
44
+ return {
45
+ twig: twigVariables,
46
+ sass: scssVariables,
47
+ js: jsVariables,
48
+ }
49
+ }
50
+
51
+ module.exports = getEnvData;
@@ -1,5 +1,6 @@
1
1
  const paths = require('../../lib/get-path');
2
2
  const merge = require('../../lib/merge');
3
+ const getEnvData = require('../env/get-env');
3
4
  const get = require('lodash/get');
4
5
  const map = require('lodash/map');
5
6
  const cloneDeep = require('lodash/cloneDeep');
@@ -83,6 +84,11 @@ module.exports = function preprocessJavascriptsConfig (config, fullConfig) {
83
84
  .replace('[folder]/', entry.outpuSubFolder ? entry.outpuSubFolder + '/' : '')
84
85
  .replace('[folder]', entry.outpuSubFolder ? entry.outpuSubFolder : '');
85
86
 
87
+ // Use process.env... variables from .env files
88
+ const envVariables = merge(getEnvData().js, {
89
+ 'process.env.NODE_ENV': JSON.stringify(global.production ? 'production' : 'development'),
90
+ });
91
+
86
92
  const buildConfig = merge(entryConfig, {
87
93
  webpack: {
88
94
  mode: global.production ? 'production' : 'development',
@@ -96,9 +102,7 @@ module.exports = function preprocessJavascriptsConfig (config, fullConfig) {
96
102
 
97
103
  // Plugins, add ENV variables
98
104
  plugins: [
99
- new webpack.DefinePlugin({
100
- 'process.env.NODE_ENV': JSON.stringify(global.production ? 'production' : 'development'),
101
- }),
105
+ new webpack.DefinePlugin(envVariables),
102
106
  new WatchExternalFilesPlugin.default({
103
107
  verbose: false,
104
108
  files: [
@@ -93,6 +93,22 @@ test('alt/other.js file doesn\'t exist', async () => {
93
93
  expect(fsPromises.stat(path.resolve(publicPath, 'assets/javascripts/alt/other.js'))).rejects.toThrow();
94
94
  });
95
95
 
96
+ test('.env and .env.local files loaded', () => {
97
+ return Promise.all([
98
+ fsPromises.readFile(path.resolve(publicPath, 'assets/javascripts/main.js'), {'encoding': 'utf8'}).then((js) => {
99
+ expect(js.indexOf('console.log("env.host ==","https://test-local.tld")')).not.toBe(-1);
100
+ expect(js.indexOf('console.log("env.foo ==","foo-global")')).not.toBe(-1);
101
+ expect(js.indexOf('console.log("env.bar ==","bar-local")')).not.toBe(-1);
102
+ }),
103
+ fsPromises.readFile(path.resolve(publicPath, 'env.html'), {'encoding': 'utf8'}).then((html) => {
104
+ expect(html.indexOf('<p>HOST: https://test-local.tld</p>')).not.toBe(-1);
105
+ }),
106
+ fsPromises.readFile(path.resolve(publicPath, 'assets/stylesheets/env-test.css'), {'encoding': 'utf8'}).then((css) => {
107
+ expect(css.indexOf('.env-test:before{content:"https://test-local.tld"}')).not.toBe(-1);
108
+ }),
109
+ ]);
110
+ });
111
+
96
112
  test('icons generated', () => {
97
113
  return fsPromises.readFile(path.resolve(publicPath, 'assets/images/icons.svg'), {'encoding': 'utf8'}).then((svg) => {
98
114
  expect(svg.indexOf('<symbol id="example-arrow">')).not.toBe(-1);
@@ -0,0 +1,168 @@
1
+ # gulp-sass Changelog
2
+
3
+ ## v5.0.0
4
+
5
+ **June 25, 2021**
6
+
7
+ <https://github.com/dlmanning/gulp-sass/releases/tag/v5.0.0>
8
+
9
+ ## v4.1.1
10
+
11
+ **June 24, 2021**
12
+
13
+ <https://github.com/dlmanning/gulp-sass/releases/tag/v4.1.1>
14
+
15
+ ## v4.1.0
16
+
17
+ **April 23, 2020**
18
+
19
+ <https://github.com/dlmanning/gulp-sass/releases/tag/v4.1.0>
20
+
21
+ ## v4.0.2
22
+
23
+ **October 16, 2018**
24
+
25
+ <https://github.com/dlmanning/gulp-sass/releases/tag/v4.0.2>
26
+
27
+ ## v4.0.1
28
+
29
+ **Apr 8, 2018**
30
+
31
+ <https://github.com/dlmanning/gulp-sass/releases/tag/v4.0.1>
32
+
33
+ ## v4.0.0
34
+
35
+ **April 5, 2018**
36
+
37
+ <https://github.com/dlmanning/gulp-sass/releases/tag/v4.0.0>
38
+
39
+ ## v3.2.1
40
+
41
+ **March 24, 2018**
42
+
43
+ <https://github.com/dlmanning/gulp-sass/releases/tag/v3.2.1>
44
+
45
+ ## v3.2.0
46
+
47
+ **March 12, 2018**
48
+
49
+ <https://github.com/dlmanning/gulp-sass/releases/tag/v3.2.0>
50
+
51
+ ## v3.1.0
52
+
53
+ **January 9, 2017**
54
+
55
+ <https://github.com/dlmanning/gulp-sass/releases/tag/v3.1.0>
56
+
57
+ ## v3.0.0
58
+
59
+ **January 9, 2017**
60
+
61
+ <https://github.com/dlmanning/gulp-sass/releases/tag/v3.0.0>
62
+
63
+ ## v2.3.2
64
+
65
+ **June 15, 2016**
66
+
67
+ <https://github.com/dlmanning/gulp-sass/releases/tag/v2.3.2>
68
+
69
+ ## v2.3.1
70
+
71
+ **April 22, 2016**
72
+
73
+ <https://github.com/dlmanning/gulp-sass/releases/tag/v2.3.1>
74
+
75
+ ## v2.3.0
76
+
77
+ **April 21, 2016**
78
+
79
+ <https://github.com/dlmanning/gulp-sass/releases/tag/v2.3.0>
80
+
81
+ ## v2.3.0-beta.1
82
+
83
+ **February 4, 2016**
84
+
85
+ <https://github.com/dlmanning/gulp-sass/releases/tag/v2.3.0-beta.1>
86
+
87
+ ## v2.2.0
88
+
89
+ **February 4, 2016**
90
+
91
+ <https://github.com/dlmanning/gulp-sass/releases/tag/v2.2.0>
92
+
93
+ ## v2.1.0
94
+
95
+ **November 2, 2015**
96
+
97
+ <https://github.com/dlmanning/gulp-sass/releases/tag/v2.1.0>
98
+
99
+ ## v2.1.0-beta
100
+
101
+ **September 21, 2015**
102
+
103
+ * **Change** Updated to `node-sass` 3.4.0-beta1
104
+
105
+ ## v2.0.4
106
+
107
+ **July 15, 2015**
108
+
109
+ * **Fix** Relative file path now uses `file.relative` instead of arcane `split('/').pop` magic. Resolves lots of issues with source map paths.
110
+ * **Fix** Empty partials no longer copied to CSS folder
111
+
112
+ ## v2.0.3
113
+
114
+ **June 27, 2015**
115
+
116
+ * **Fix** Empty partials are no longer copied to CSS folder
117
+
118
+ ## v2.0.2
119
+
120
+ **June 25, 2015**
121
+
122
+ * **Fix** Error in watch stream preventing watch from continuing
123
+
124
+ ## v2.0.1
125
+
126
+ **May 13, 2015**
127
+
128
+ * **Fix** Source maps now work as expected with Autoprefixer
129
+ * **Fix** Current file directory `unshift` onto includePaths stack so it's checked first
130
+ * **Fix** Error message returned is unformatted so as to not break other error handling (*i.e.* `gulp-notify`)
131
+
132
+ ## v2.0.0
133
+
134
+ **May 6, 2015**
135
+
136
+ * **Change** Updated to `node-sass` 3.0.0
137
+
138
+ ## v2.0.0-alpha.1
139
+
140
+ **March 26, 2015**
141
+
142
+ * **New** Added `renderSync` option that can be used through `sass.sync()`
143
+
144
+ ### March 24, 2015
145
+
146
+ * **Change** Updated to `node-sass` 3.0.0-alpha.1
147
+ * **New** Added support for `gulp-sourcemaps` including tests
148
+ * **New** Added `.editorconfig` for development consistency
149
+ * **New** Added linting and test for said linting
150
+ * **Change** Updated the README
151
+ * **New** `logError` function to make streaming errors possible instead of breaking the stream
152
+
153
+ ### 1.3.3
154
+
155
+ * updated to `node-sass` 2.0 (final)
156
+ * should now work with Node.js 0.12 and io.js
157
+
158
+ ### 1.3.2
159
+
160
+ * fixed `errLogToConsole`
161
+
162
+ ### 1.3.1
163
+
164
+ * bug fix
165
+
166
+ ## Version 1.3.0
167
+
168
+ * Supports `node-sass` 2.0 (thanks laurelnaiad!)
@@ -0,0 +1,20 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2013 David Manning
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ 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, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,196 @@
1
+ # gulp-sass ![npm package version](https://img.shields.io/npm/v/gulp-sass?label=npm%20version) [![Build Status](https://img.shields.io/github/workflow/status/dlmanning/gulp-sass/CI/master)](https://github.com/dlmanning/gulp-sass/actions?query=workflow%3ACI+branch%3Amaster) [![Join the chat at https://gitter.im/dlmanning/gulp-sass](https://img.shields.io/gitter/room/dlmanning/gulp-sass?color=%2346b091&label=chat&logo=gitter)](https://gitter.im/dlmanning/gulp-sass) ![Node.js support](https://img.shields.io/node/v/gulp-sass)
2
+
3
+ Sass plugin for [Gulp](https://github.com/gulpjs/gulp).
4
+
5
+ **_Before filing an issue, please make sure you have [updated to the latest version of `gulp-sass`](https://github.com/dlmanning/gulp-sass/wiki/Update-to-the-latest-Gulp-Sass) and have gone through our [Common Issues and Their Fixes](https://github.com/dlmanning/gulp-sass/wiki/Common-Issues-and-Their-Fixes) section._**
6
+
7
+ **Migrating your existing project to version 5? Please read our (short!) [migration guide](#migrating-to-version-5).**
8
+
9
+ ## Support
10
+
11
+ Only [Active LTS and Current releases](https://github.com/nodejs/Release#release-schedule) are supported.
12
+
13
+ ## Installation
14
+
15
+ To use `gulp-sass`, you must install both `gulp-sass` itself *and* a Sass compiler. `gulp-sass` supports both [Dart Sass][] and [Node Sass][], although Node Sass is [deprecated](https://sass-lang.com/blog/libsass-is-deprecated). We recommend that you use Dart Sass for new projects, and migrate Node Sass projects to Dart Sass when possible.
16
+
17
+ Whichever compiler you choose, it's best to install these as dev dependencies:
18
+
19
+ ```sh
20
+ npm install sass gulp-sass --save-dev
21
+ ```
22
+
23
+ ### Importing it into your project
24
+
25
+ `gulp-sass` must be imported into your gulpfile, where you provide it the compiler of your choice. To use `gulp-sass` in a CommonJS module (which is most Node.js environments), do something like this:
26
+
27
+ ```js
28
+ const sass = require('gulp-sass')(require('sass'));
29
+ ```
30
+
31
+ To use `gulp-sass` in an ECMAScript module (which is supported in newer Node.js 14 and later), do something like this:
32
+
33
+ ```js
34
+ import dartSass from 'sass';
35
+ import gulpSass from 'gulp-sass';
36
+ const sass = gulpSass(dartSass);
37
+ ```
38
+
39
+ ## Usage
40
+
41
+ **Note:** These examples are written for CommonJS modules and assume you're using Gulp 4. For examples that work with Gulp 3, [check the docs for an earlier version of `gulp-sass`](https://github.com/dlmanning/gulp-sass/tree/v4.1.1).
42
+
43
+ `gulp-sass` must be used in a Gulp task. Your task can call `sass()` (to asynchronously render your CSS), or `sass.sync()` (to synchronously render your CSS). Then, export your task with the `export` keyword. We'll show some examples of how to do that.
44
+
45
+ **⚠️ Note:** When using Dart Sass, **synchronous rendering is twice as fast as asynchronous rendering**. The Sass team is exploring ways to improve asynchronous rendering with Dart Sass, but for now, you will get the best performance from `sass.sync()`. If performance is critical, you can use `node-sass` instead, but bear in mind that `node-sass` may not support modern Sass features you rely on.
46
+
47
+ ### Render your CSS
48
+
49
+ To render your CSS with a build task, then watch your files for changes, you might write something like this:
50
+
51
+ ```js
52
+ 'use strict';
53
+
54
+ const gulp = require('gulp');
55
+ const sass = require('gulp-sass')(require('sass'));
56
+
57
+ function buildStyles() {
58
+ return gulp.src('./sass/**/*.scss')
59
+ .pipe(sass().on('error', sass.logError))
60
+ .pipe(gulp.dest('./css'));
61
+ };
62
+
63
+ exports.buildStyles = buildStyles;
64
+ exports.watch = function () {
65
+ gulp.watch('./sass/**/*.scss', ['sass']);
66
+ };
67
+ ```
68
+
69
+ With synchronous rendering, that Gulp task looks like this:
70
+
71
+ ```js
72
+ function buildStyles() {
73
+ return gulp.src('./sass/**/*.scss')
74
+ .pipe(sass.sync().on('error', sass.logError))
75
+ .pipe(gulp.dest('./css'));
76
+ };
77
+ ```
78
+
79
+ ### Render with options
80
+
81
+ To change the final output of your CSS, you can pass an options object to your renderer. `gulp-sass` supports [Node Sass's render options](https://github.com/sass/node-sass#options), with two unsupported exceptions:
82
+
83
+ - The `data` option, which is used by `gulp-sass` internally.
84
+ - The `file` option, which has undefined behavior that may change without notice.
85
+
86
+ For example, to compress your CSS, you can call `sass({outputStyle: 'compressed'}`. In the context of a Gulp task, that looks like this:
87
+
88
+ ```js
89
+ function buildStyles() {
90
+ return gulp.src('./sass/**/*.scss')
91
+ .pipe(sass({outputStyle: 'compressed'}).on('error', sass.logError))
92
+ .pipe(gulp.dest('./css'));
93
+ };
94
+
95
+ exports.buildStyles = buildStyles;
96
+ ```
97
+
98
+ Or this for synchronous rendering:
99
+
100
+ ```js
101
+ function buildStyles() {
102
+ return gulp.src('./sass/**/*.scss')
103
+ .pipe(sass.sync({outputStyle: 'compressed'}).on('error', sass.logError))
104
+ .pipe(gulp.dest('./css'));
105
+ };
106
+
107
+ exports.buildStyles = buildStyles;
108
+ ```
109
+
110
+ ### Include a source map
111
+
112
+ `gulp-sass` can be used in tandem with [`gulp-sourcemaps`](https://github.com/gulp-sourcemaps/gulp-sourcemaps) to generate source maps for the Sass-to-CSS compilation. You will need to initialize `gulp-sourcemaps` _before_ running `gulp-sass`, and write the source maps after.
113
+
114
+ ```js
115
+ const sourcemaps = require('gulp-sourcemaps');
116
+
117
+ function buildStyles() {
118
+ return gulp.src('./sass/**/*.scss')
119
+ .pipe(sourcemaps.init())
120
+ .pipe(sass().on('error', sass.logError))
121
+ .pipe(sourcemaps.write())
122
+ .pipe(gulp.dest('./css'));
123
+ }
124
+
125
+ exports.buildStyles = buildStyles;
126
+ ```
127
+
128
+ By default, `gulp-sourcemaps` writes the source maps inline, in the compiled CSS files. To write them to a separate file, specify a path relative to the `gulp.dest()` destination in the `sourcemaps.write()` function.
129
+
130
+ ```js
131
+ const sourcemaps = require('gulp-sourcemaps');
132
+
133
+ function buildStyles() {
134
+ return gulp.src('./sass/**/*.scss')
135
+ .pipe(sourcemaps.init())
136
+ .pipe(sass().on('error', sass.logError))
137
+ .pipe(sourcemaps.write('./maps'))
138
+ .pipe(gulp.dest('./css'));
139
+ };
140
+
141
+ exports.buildStyles = buildStyles;
142
+ ```
143
+
144
+ <h2 id="migrating-to-version-5">Migrating to version 5</h2>
145
+
146
+ `gulp-sass` version 5 requires Node.js 12 or later, and introduces some breaking changes. Additionally, changes in Node.js itself mean that Node fibers can no longer be used to speed up Dart Sass in Node.js 16.
147
+
148
+ ### Setting a Sass compiler
149
+
150
+ As of version 5, `gulp-sass` _does not include a default Sass compiler_, so you must install one (either `node-sass` or `sass`) along with `gulp-sass`.
151
+
152
+ ```sh
153
+ npm install sass gulp-sass --save-dev
154
+ ```
155
+
156
+ Then, you must explicitly set that compiler in your gulpfille. Instead of setting a `compiler` prop on the `gulp-sass` instance, you pass the compiler into a function call when instantiating `gulp-sass`.
157
+
158
+ These changes look something like this:
159
+
160
+ ```diff
161
+ - const sass = require('gulp-sass'));
162
+ - const compiler = require('sass');
163
+ - sass.compiler = compiler;
164
+ + const sass = require('gulp-sass')(require('sass'));
165
+ ```
166
+
167
+ If you're migrating an ECMAScript module, that'll look something like this:
168
+
169
+ ```diff
170
+ import dartSass from 'sass';
171
+ - import sass from 'gulp-sass';
172
+ - sass.compiler = dartSass;
173
+
174
+ import dartSass from 'sass';
175
+ + import gulpSass from 'gulp-sass';
176
+ + const sass = gulpSass(dartSass);
177
+ ```
178
+
179
+ ### What about fibers?
180
+
181
+ We used to recommend Node fibers as a way to speed up asynchronous rendering with Dart Sass. Unfortunately, [Node fibers are discontinued](https://sass-lang.com/blog/node-fibers-discontinued) and will not work in Node.js 16. The Sass team is exploring its options for future performance improvements, but for now, you will get the best performance from `sass.sync()`.
182
+
183
+ ## Issues
184
+
185
+ `gulp-sass` is a light-weight wrapper around either [Dart Sass][] or [Node Sass][] (which in turn is a Node.js binding for [LibSass][]. Because of this, the issue you're having likely isn't a `gulp-sass` issue, but an issue with one those projects or with [Sass][] as a whole.
186
+
187
+ If you have a feature request/question about how Sass works/concerns on how your Sass gets compiled/errors in your compiling, it's likely a Dart Sass or LibSass issue and you should file your issue with one of those projects.
188
+
189
+ If you're having problems with the options you're passing in, it's likely a Dart Sass or Node Sass issue and you should file your issue with one of those projects.
190
+
191
+ We may, in the course of resolving issues, direct you to one of these other projects. If we do so, please follow up by searching that project's issue queue (both open and closed) for your problem and, if it doesn't exist, filing an issue with them.
192
+
193
+ [Dart Sass]: https://sass-lang.com/dart-sass
194
+ [LibSass]: https://sass-lang.com/libsass
195
+ [Node Sass]: https://github.com/sass/node-sass
196
+ [Sass]: https://sass-lang.com
@@ -0,0 +1,225 @@
1
+ // 2025-01-20, Kaspars Zuks: added "options.data" support for variables
2
+ 'use strict';
3
+
4
+ const path = require('path');
5
+ const { Transform } = require('stream');
6
+ const picocolors = require('picocolors');
7
+ const PluginError = require('plugin-error');
8
+ const replaceExtension = require('replace-ext');
9
+ const stripAnsi = require('strip-ansi');
10
+ const clonedeep = require('lodash.clonedeep');
11
+ const applySourceMap = require('vinyl-sourcemaps-apply');
12
+
13
+ const PLUGIN_NAME = 'gulp-sass';
14
+
15
+ const MISSING_COMPILER_MESSAGE = `
16
+ gulp-sass no longer has a default Sass compiler; please set one yourself.
17
+ Both the "sass" and "node-sass" packages are permitted.
18
+ For example, in your gulpfile:
19
+
20
+ const sass = require('gulp-sass')(require('sass'));
21
+ `;
22
+
23
+ const transfob = (transform) => new Transform({ transform, objectMode: true });
24
+
25
+ /**
26
+ * Handles returning the file to the stream
27
+ */
28
+ const filePush = (file, sassObject, callback) => {
29
+ // Build Source Maps!
30
+ if (sassObject.map) {
31
+ // Transform map into JSON
32
+ const sassMap = JSON.parse(sassObject.map.toString());
33
+ // Grab the stdout and transform it into stdin
34
+ const sassMapFile = sassMap.file.replace(/^stdout$/, 'stdin');
35
+ // Grab the base filename that's being worked on
36
+ const sassFileSrc = file.relative;
37
+ // Grab the path portion of the file that's being worked on
38
+ const sassFileSrcPath = path.dirname(sassFileSrc);
39
+
40
+ if (sassFileSrcPath) {
41
+ const sourceFileIndex = sassMap.sources.indexOf(sassMapFile);
42
+ // Prepend the path to all files in the sources array except the file that's being worked on
43
+ sassMap.sources = sassMap.sources.map((source, index) => (
44
+ index === sourceFileIndex
45
+ ? source
46
+ : path.join(sassFileSrcPath, source)
47
+ ));
48
+ }
49
+
50
+ // Remove 'stdin' from souces and replace with filenames!
51
+ sassMap.sources = sassMap.sources.filter((src) => src !== 'stdin' && src);
52
+
53
+ // Replace the map file with the original filename (but new extension)
54
+ sassMap.file = replaceExtension(sassFileSrc, '.css');
55
+ // Apply the map
56
+ applySourceMap(file, sassMap);
57
+ }
58
+
59
+ file.contents = sassObject.css;
60
+ file.path = replaceExtension(file.path, '.css');
61
+
62
+ if (file.stat) {
63
+ file.stat.atime = file.stat.mtime = file.stat.ctime = new Date();
64
+ }
65
+
66
+ callback(null, file);
67
+ };
68
+
69
+ /**
70
+ * Handles error message
71
+ */
72
+ const handleError = (error, file, callback) => {
73
+ const filePath = (error.file === 'stdin' ? file.path : error.file) || file.path;
74
+ const relativePath = path.relative(process.cwd(), filePath);
75
+ const message = `${picocolors.underline(relativePath)}\n${error.formatted}`;
76
+
77
+ error.messageFormatted = message;
78
+ error.messageOriginal = error.message;
79
+ error.message = stripAnsi(message);
80
+ error.relativePath = relativePath;
81
+
82
+ return callback(new PluginError(PLUGIN_NAME, error));
83
+ };
84
+
85
+ /**
86
+ * Escape SCSS variable value for output in SCSS
87
+ * @param {any} value Value
88
+ * @returns {string} Escaped value
89
+ */
90
+ const escapeSCSSVariable = (value) => {
91
+ if (value === true || value === false || !isNaN(value)) {
92
+ return value;
93
+ } else {
94
+ // Convert to string
95
+ return "'" + value.toString().replace(/\\/g, '\\\\').replace(/'/g, '\\\'').replace(/\n/g, '\\n') + "'";
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Main Gulp Sass function
101
+ */
102
+
103
+ // eslint-disable-next-line arrow-body-style
104
+ const gulpSass = (options, sync) => {
105
+ return transfob((file, encoding, callback) => {
106
+ if (file.isNull()) {
107
+ callback(null, file);
108
+ return;
109
+ }
110
+
111
+ if (file.isStream()) {
112
+ callback(new PluginError(PLUGIN_NAME, 'Streaming not supported'));
113
+ return;
114
+ }
115
+
116
+ if (path.basename(file.path).startsWith('_')) {
117
+ callback();
118
+ return;
119
+ }
120
+
121
+ if (!file.contents.length) {
122
+ file.path = replaceExtension(file.path, '.css');
123
+ callback(null, file);
124
+ return;
125
+ }
126
+
127
+ const opts = clonedeep(options || {});
128
+ opts.data = file.contents.toString();
129
+
130
+ // Stringiyfy variables
131
+ if (options.data) {
132
+ const scssVariables = [];
133
+
134
+ if (typeof options.data === 'string') {
135
+ // Assume it's valid SCSS
136
+ scssVariables.push(options.data);
137
+ } else {
138
+ for (let key in options.data) {
139
+ const value = escapeSCSSVariable(options.data[key]);
140
+ scssVariables.push(`$${key}: ${value};`);
141
+ }
142
+ }
143
+
144
+ opts.data = scssVariables.join('') + opts.data;
145
+ }
146
+
147
+ // We set the file path here so that libsass can correctly resolve import paths
148
+ opts.file = file.path;
149
+
150
+ // Ensure `indentedSyntax` is true if a `.sass` file
151
+ if (path.extname(file.path) === '.sass') {
152
+ opts.indentedSyntax = true;
153
+ }
154
+
155
+ // Ensure file's parent directory in the include path
156
+ if (opts.includePaths) {
157
+ if (typeof opts.includePaths === 'string') {
158
+ opts.includePaths = [opts.includePaths];
159
+ }
160
+ } else {
161
+ opts.includePaths = [];
162
+ }
163
+
164
+ opts.includePaths.unshift(path.dirname(file.path));
165
+
166
+ // Generate Source Maps if the source-map plugin is present
167
+ if (file.sourceMap) {
168
+ opts.sourceMap = file.path;
169
+ opts.omitSourceMapUrl = true;
170
+ opts.sourceMapContents = true;
171
+ }
172
+
173
+ if (sync !== true) {
174
+ /**
175
+ * Async Sass render
176
+ */
177
+ gulpSass.compiler.render(opts, (error, obj) => {
178
+ if (error) {
179
+ handleError(error, file, callback);
180
+ return;
181
+ }
182
+
183
+ filePush(file, obj, callback);
184
+ });
185
+ } else {
186
+ /**
187
+ * Sync Sass render
188
+ */
189
+ try {
190
+ filePush(file, gulpSass.compiler.renderSync(opts), callback);
191
+ } catch (error) {
192
+ handleError(error, file, callback);
193
+ }
194
+ }
195
+ });
196
+ };
197
+
198
+ /**
199
+ * Sync Sass render
200
+ */
201
+ gulpSass.sync = (options) => gulpSass(options, true);
202
+
203
+ /**
204
+ * Log errors nicely
205
+ */
206
+ gulpSass.logError = function logError(error) {
207
+ const message = new PluginError('sass', error.messageFormatted).toString();
208
+ process.stderr.write(`${message}\n`);
209
+ this.emit('end');
210
+ };
211
+
212
+ module.exports = (compiler) => {
213
+ if (!compiler || !compiler.render) {
214
+ const message = new PluginError(
215
+ PLUGIN_NAME,
216
+ MISSING_COMPILER_MESSAGE,
217
+ { showProperties: false },
218
+ ).toString();
219
+ process.stderr.write(`${message}\n`);
220
+ process.exit(1);
221
+ }
222
+
223
+ gulpSass.compiler = compiler;
224
+ return gulpSass;
225
+ };
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "gulp-sass",
3
+ "version": "5.1.0",
4
+ "description": "Gulp plugin for sass",
5
+ "main": "index.js",
6
+ "engines": {
7
+ "node": ">=12"
8
+ },
9
+ "scripts": {
10
+ "lint": "eslint --report-unused-disable-directives --ignore-path .gitignore .",
11
+ "fix": "npm run lint -- --fix",
12
+ "mocha": "mocha",
13
+ "test": "npm run test:node-sass && npm run test:dart-sass",
14
+ "test:node-sass": "mocha",
15
+ "test:dart-sass": "mocha -- --sass"
16
+ },
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/dlmanning/gulp-sass.git"
20
+ },
21
+ "keywords": [
22
+ "gulpplugin",
23
+ "sass",
24
+ "gulp"
25
+ ],
26
+ "author": "David Manning",
27
+ "license": "MIT",
28
+ "bugs": {
29
+ "url": "https://github.com/dlmanning/gulp-sass/issues"
30
+ },
31
+ "homepage": "https://github.com/dlmanning/gulp-sass#readme",
32
+ "files": [
33
+ "index.js"
34
+ ],
35
+ "dependencies": {
36
+ "lodash.clonedeep": "^4.5.0",
37
+ "picocolors": "^1.0.0",
38
+ "plugin-error": "^1.0.1",
39
+ "replace-ext": "^2.0.0",
40
+ "strip-ansi": "^6.0.1",
41
+ "vinyl-sourcemaps-apply": "^0.2.1"
42
+ },
43
+ "devDependencies": {
44
+ "autoprefixer": "^10.4.0",
45
+ "eslint": "^8.5.0",
46
+ "eslint-config-airbnb-base": "^15.0.0",
47
+ "eslint-plugin-import": "^2.25.3",
48
+ "globule": "^1.3.3",
49
+ "gulp": "^4.0.2",
50
+ "gulp-postcss": "^9.0.1",
51
+ "gulp-sourcemaps": "^3.0.0",
52
+ "gulp-tap": "^2.0.0",
53
+ "mocha": "^9.1.3",
54
+ "node-sass": "^7.0.1",
55
+ "postcss": "^8.4.5",
56
+ "rimraf": "^3.0.2",
57
+ "sass": "^1.45.1",
58
+ "vinyl": "^2.2.1"
59
+ }
60
+ }
@@ -43,7 +43,7 @@ module.exports = function (options) {
43
43
  twig = Twig.twig,
44
44
  twigOpts = {
45
45
  path: file.path,
46
- async: false
46
+ async: false,
47
47
  },
48
48
  template;
49
49
 
@@ -59,6 +59,9 @@ module.exports = function (options) {
59
59
  if (options.namespaces !== undefined) {
60
60
  twigOpts.namespaces = options.namespaces;
61
61
  }
62
+ if (options.rethrow !== undefined) {
63
+ twigOpts.rethrow = options.rethrow;
64
+ }
62
65
  if (options.cache !== true) {
63
66
  Twig.cache(false);
64
67
  }