@moritzloewenstein/vite-plugin-sass-glob-import 5.0.0 → 5.0.2

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 CHANGED
@@ -12,14 +12,14 @@ Fork of [vite-plugin-sass-glob-import](https://github.com/cmalven/vite-plugin-sa
12
12
  ## Install
13
13
 
14
14
  ```shell
15
- npm i -D vite-plugin-sass-glob-import
15
+ npm i -D @moritzloewenstein/vite-plugin-sass-glob-import
16
16
  ```
17
17
 
18
18
  ```js
19
19
  // In vite.config.js
20
20
 
21
21
  import { defineConfig } from "vite";
22
- import sassGlobImports from "vite-plugin-sass-glob-import";
22
+ import sassGlobImports from "@moritzloewenstein/vite-plugin-sass-glob-import";
23
23
 
24
24
  export default defineConfig({
25
25
  plugins: [sassGlobImports()],
@@ -0,0 +1,10 @@
1
+ import { Plugin } from 'vite';
2
+
3
+ interface PluginOptions {
4
+ ignorePaths?: string[];
5
+ autoInvalidation?: boolean;
6
+ }
7
+
8
+ declare function sassGlobImports(_options?: PluginOptions): Plugin;
9
+
10
+ export { sassGlobImports as default };
package/dist/index.mjs ADDED
@@ -0,0 +1,160 @@
1
+ // src/index.ts
2
+ import fs from "fs";
3
+ import path from "path";
4
+ import c from "ansi-colors";
5
+ import { globSync } from "glob";
6
+ import { minimatch } from "minimatch";
7
+ import {
8
+ normalizePath
9
+ } from "vite";
10
+ var IMPORT_REGEX;
11
+ var options;
12
+ var globToModuleIds;
13
+ var projectRoot;
14
+ var server;
15
+ function sassGlobImports(_options = {}) {
16
+ IMPORT_REGEX = /^([ \t]*(?:\/\*.*)?)@(import|use)\s+["']([^"']+\*[^"']*(?:\.scss|\.sass)?)["'];?([ \t]*(?:\/[/*].*)?)$/gm;
17
+ options = _options;
18
+ globToModuleIds = /* @__PURE__ */ new Map();
19
+ return {
20
+ name: "sass-glob-import",
21
+ enforce: "pre",
22
+ configResolved(config) {
23
+ projectRoot = normalizePath(config.root);
24
+ if (options.autoInvalidation && !config.server.watch) {
25
+ config.logger.warn(
26
+ "vite-plugin-sass-glob-import: set server.watch: true for automatic glob module invalidation"
27
+ );
28
+ }
29
+ },
30
+ configureServer(_server) {
31
+ if (!options.autoInvalidation) {
32
+ return;
33
+ }
34
+ server = _server;
35
+ server.watcher.on("all", async (_event, pathName) => {
36
+ if (!path.extname(pathName).match(/\.sass|\.scss/i)) {
37
+ return;
38
+ }
39
+ const relPathName = path.relative(projectRoot, pathName);
40
+ const modsToInvalidate = /* @__PURE__ */ new Set();
41
+ for (const [glob, modSet] of globToModuleIds) {
42
+ if (minimatch(relPathName, glob)) {
43
+ for (const mod of modSet) {
44
+ modsToInvalidate.add(mod);
45
+ }
46
+ }
47
+ }
48
+ const modsByFile = /* @__PURE__ */ new Set();
49
+ for (const mod of modsToInvalidate) {
50
+ const modules = server.moduleGraph.getModulesByFile(mod);
51
+ if (!modules) continue;
52
+ for (const m of modules) {
53
+ modsByFile.add(m);
54
+ }
55
+ }
56
+ invalidateModules(server, modsByFile, Date.now());
57
+ await Promise.all(
58
+ Array.from(modsByFile).map((mod) => server.reloadModule(mod))
59
+ );
60
+ });
61
+ },
62
+ transform(src, id) {
63
+ return {
64
+ code: transform(src, id),
65
+ map: null
66
+ // provide source map if available
67
+ };
68
+ }
69
+ };
70
+ }
71
+ function invalidateModules(server2, modules, timestamp) {
72
+ const seen = /* @__PURE__ */ new Set();
73
+ for (const mod of modules) {
74
+ server2.moduleGraph.invalidateModule(mod, seen, timestamp, true);
75
+ }
76
+ return modules;
77
+ }
78
+ function isSassOrScss(filename) {
79
+ return !fs.statSync(filename).isDirectory() && path.extname(filename).match(/\.sass|\.scss/i);
80
+ }
81
+ function transform(src, id) {
82
+ const fileName = path.basename(id);
83
+ const filePath = path.dirname(id);
84
+ const isSass = path.extname(fileName).match(/\.sass/i);
85
+ const searchBases = [filePath];
86
+ const ignorePaths = options.ignorePaths || [];
87
+ const contentLinesCount = src.split("\n").length;
88
+ for (let i = 0; i < contentLinesCount; i++) {
89
+ const result = [...src.matchAll(IMPORT_REGEX)];
90
+ if (result.length === 0) continue;
91
+ const [importRule, startComment, importType, globPattern, endComment] = result[0];
92
+ if (options.autoInvalidation) {
93
+ const projectGlob = path.relative(
94
+ projectRoot,
95
+ path.resolve(path.join(filePath, globPattern))
96
+ );
97
+ const hasGlob = globToModuleIds.has(projectGlob);
98
+ if (!globToModuleIds.get(projectGlob)?.has(id)) {
99
+ const modSet = globToModuleIds.get(projectGlob) ?? /* @__PURE__ */ new Set();
100
+ modSet.add(id);
101
+ globToModuleIds.set(projectGlob, modSet);
102
+ if (!hasGlob) {
103
+ const globDir = projectGlob.split("*")[0];
104
+ server.watcher.add(globDir);
105
+ }
106
+ }
107
+ }
108
+ let files = [];
109
+ let basePath = "";
110
+ for (let i2 = 0; i2 < searchBases.length; i2++) {
111
+ basePath = searchBases[i2];
112
+ files = globSync(path.join(basePath, globPattern), {
113
+ cwd: "./",
114
+ windowsPathsNoEscape: true
115
+ }).sort((a, b) => a.localeCompare(b, "en"));
116
+ const globPatternWithoutWildcard = globPattern.split("*")[0];
117
+ if (globPatternWithoutWildcard.length) {
118
+ const directoryExists = fs.existsSync(
119
+ path.join(basePath, globPatternWithoutWildcard)
120
+ );
121
+ if (!directoryExists) {
122
+ console.warn(
123
+ c.yellow(
124
+ `Sass Glob Import: Directories don't exist for the glob pattern "${globPattern}"`
125
+ )
126
+ );
127
+ }
128
+ }
129
+ if (files.length > 0) {
130
+ break;
131
+ }
132
+ }
133
+ const isGlobTrailStatic = !globPattern.split("/").at(-1)?.includes("*");
134
+ const imports = [];
135
+ files.forEach((anyFilename, index) => {
136
+ if (!isSassOrScss(anyFilename)) {
137
+ return;
138
+ }
139
+ const scssFilename = path.relative(basePath, anyFilename).replace(/\\/g, "/").replace(/^\//, "");
140
+ if (!ignorePaths.some((ignorePath) => {
141
+ return minimatch(scssFilename, ignorePath);
142
+ })) {
143
+ const file = isGlobTrailStatic ? `"${scssFilename}" as ${path.parse(scssFilename).name}_${index}` : `"${scssFilename}"`;
144
+ imports.push(`@${importType} ${file}${isSass ? "" : ";"}`);
145
+ }
146
+ });
147
+ if (startComment) {
148
+ imports.unshift(startComment);
149
+ }
150
+ if (endComment) {
151
+ imports.push(endComment);
152
+ }
153
+ const replaceString = imports.join("\n");
154
+ src = src.replace(importRule, replaceString);
155
+ }
156
+ return src;
157
+ }
158
+ export {
159
+ sassGlobImports as default
160
+ };
package/package.json CHANGED
@@ -1,9 +1,12 @@
1
1
  {
2
2
  "name": "@moritzloewenstein/vite-plugin-sass-glob-import",
3
- "version": "5.0.0",
3
+ "version": "5.0.2",
4
4
  "description": "Use glob syntax for imports in your main Sass or SCSS file.",
5
5
  "main": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.mts",
7
+ "files": [
8
+ "dist"
9
+ ],
7
10
  "scripts": {
8
11
  "dev": "vitest",
9
12
  "build": "tsup src/index.ts --format esm --dts",
@@ -1,34 +0,0 @@
1
- name: Lint and Test
2
-
3
- on:
4
- push:
5
- branches: [ main ]
6
- pull_request:
7
- branches: [ main ]
8
-
9
- jobs:
10
- build:
11
- runs-on: ubuntu-latest
12
-
13
- steps:
14
- - uses: actions/checkout@v5
15
-
16
- - uses: actions/setup-node@v5
17
- with:
18
- node-version: 22.x
19
- cache: 'npm'
20
-
21
- - uses: actions/cache@v4
22
- with:
23
- path: ~/.npm
24
- key: npm-${{ hashFiles('package-lock.json') }}
25
- restore-keys: npm-
26
-
27
- - name: Install dependencies
28
- run: npm ci
29
-
30
- - name: Run Biome
31
- run: npm run biome:ci
32
-
33
- - name: Run tests
34
- run: npm run test
@@ -1,38 +0,0 @@
1
- name: Publish
2
-
3
- on:
4
- workflow_run:
5
- workflows: ["Lint and Test"]
6
- branches: [main]
7
- types:
8
- - completed
9
-
10
- permissions:
11
- id-token: write
12
- contents: read
13
-
14
- jobs:
15
- publish:
16
- if: ${{ github.event.workflow_run.conclusion == 'success' }}
17
- runs-on: ubuntu-latest
18
-
19
- steps:
20
- - uses: actions/checkout@v5
21
- - uses: actions/setup-node@v5
22
- with:
23
- node-version: '22.x'
24
- registry-url: 'https://registry.npmjs.org'
25
- - uses: actions/cache@v4
26
- with:
27
- path: ~/.npm
28
- key: npm-${{ hashFiles('package-lock.json') }}
29
- restore-keys: npm-
30
- - name: Install deps and build
31
- run: |
32
- set -e
33
- npm ci
34
- npm run build
35
- - name: Publish to npm
36
- run: npm publish --tag latest --access public --provenance
37
- env:
38
- NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
package/.nvmrc DELETED
@@ -1 +0,0 @@
1
- 20
package/biome.json DELETED
@@ -1,54 +0,0 @@
1
- {
2
- "$schema": "node_modules/@biomejs/biome/configuration_schema.json",
3
- "assist": { "actions": { "source": { "organizeImports": "on" } } },
4
- "files": {
5
- "ignoreUnknown": true,
6
- "includes": ["**", "!dist"]
7
- },
8
- "vcs": {
9
- "enabled": true,
10
- "clientKind": "git",
11
- "defaultBranch": "main",
12
- "useIgnoreFile": true
13
- },
14
- "linter": {
15
- "enabled": true,
16
- "rules": {
17
- "recommended": true,
18
- "complexity": {
19
- "noForEach": "off"
20
- },
21
- "style": {
22
- "noParameterAssign": "error",
23
- "useAsConstAssertion": "error",
24
- "useDefaultParameterLast": "error",
25
- "useEnumInitializers": "error",
26
- "useSelfClosingElements": "error",
27
- "useSingleVarDeclarator": "error",
28
- "noUnusedTemplateLiteral": "error",
29
- "useNumberNamespace": "error",
30
- "noInferrableTypes": "error",
31
- "noUselessElse": "error"
32
- }
33
- }
34
- },
35
- "formatter": {
36
- "enabled": true
37
- },
38
- "css": {
39
- "formatter": {
40
- "enabled": false
41
- },
42
- "linter": {
43
- "enabled": false
44
- }
45
- },
46
- "graphql": {
47
- "formatter": {
48
- "enabled": false
49
- },
50
- "linter": {
51
- "enabled": false
52
- }
53
- }
54
- }
package/src/index.ts DELETED
@@ -1,122 +0,0 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
- import c from "ansi-colors";
4
- import { globSync } from "glob";
5
- import { minimatch } from "minimatch";
6
- import type { Plugin } from "vite";
7
- import type { PluginOptions, TransformResult } from "./types";
8
-
9
- let IMPORT_REGEX: RegExp;
10
- let options: PluginOptions;
11
- export default function sassGlobImports(_options: PluginOptions = {}): Plugin {
12
- IMPORT_REGEX =
13
- /^([ \t]*(?:\/\*.*)?)@(import|use)\s+["']([^"']+\*[^"']*(?:\.scss|\.sass)?)["'];?([ \t]*(?:\/[/*].*)?)$/gm;
14
- options = _options;
15
- return {
16
- name: "sass-glob-import",
17
- enforce: "pre",
18
- transform(src: string, id: string): TransformResult {
19
- const fileName = path.basename(id);
20
- const filePath = path.dirname(id);
21
- return {
22
- code: transform(src, fileName, filePath),
23
- map: null, // provide source map if available
24
- };
25
- },
26
- };
27
- }
28
-
29
- function isSassOrScss(filename: string) {
30
- return (
31
- !fs.statSync(filename).isDirectory() &&
32
- path.extname(filename).match(/\.sass|\.scss/i)
33
- );
34
- }
35
-
36
- function transform(src: string, fileName: string, filePath: string): string {
37
- // Determine if this is Sass (vs SCSS) based on file extension
38
- const isSass = path.extname(fileName).match(/\.sass/i);
39
-
40
- // Store base locations
41
- const searchBases = [filePath];
42
- const ignorePaths = options.ignorePaths || [];
43
- const contentLinesCount = src.split("\n").length;
44
-
45
- for (let i = 0; i < contentLinesCount; i++) {
46
- const result = [...src.matchAll(IMPORT_REGEX)];
47
- if (result.length === 0) continue;
48
-
49
- const [importRule, startComment, importType, globPattern, endComment] =
50
- result[0];
51
-
52
- let files: string[] = [];
53
- let basePath = "";
54
- for (let i = 0; i < searchBases.length; i++) {
55
- basePath = searchBases[i];
56
-
57
- files = globSync(path.join(basePath, globPattern), {
58
- cwd: "./",
59
- windowsPathsNoEscape: true,
60
- }).sort((a, b) => a.localeCompare(b, "en"));
61
-
62
- // Do directories exist matching the glob pattern?
63
- const globPatternWithoutWildcard = globPattern.split("*")[0];
64
- if (globPatternWithoutWildcard.length) {
65
- const directoryExists = fs.existsSync(
66
- path.join(basePath, globPatternWithoutWildcard),
67
- );
68
- if (!directoryExists) {
69
- console.warn(
70
- c.yellow(
71
- `Sass Glob Import: Directories don't exist for the glob pattern "${globPattern}"`,
72
- ),
73
- );
74
- }
75
- }
76
-
77
- if (files.length > 0) {
78
- break;
79
- }
80
- }
81
-
82
- const isGlobTrailStatic = !globPattern.split("/").at(-1)?.includes("*");
83
- const imports = [];
84
- files.forEach((anyFilename: string, index: number) => {
85
- if (!isSassOrScss(anyFilename)) {
86
- return;
87
- }
88
-
89
- const scssFilename = path
90
- // Remove parent base path
91
- .relative(basePath, anyFilename)
92
- .replace(/\\/g, "/")
93
- // Remove leading slash
94
- .replace(/^\//, "");
95
- if (
96
- !ignorePaths.some((ignorePath: string) => {
97
- return minimatch(scssFilename, ignorePath);
98
- })
99
- ) {
100
- const file = isGlobTrailStatic
101
- ? `"${scssFilename}" as ${path.parse(scssFilename).name}_${index}`
102
- : `"${scssFilename}"`;
103
- imports.push(`@${importType} ${file}${isSass ? "" : ";"}`);
104
- }
105
- });
106
-
107
- if (startComment) {
108
- imports.unshift(startComment);
109
- }
110
-
111
- if (endComment) {
112
- imports.push(endComment);
113
- }
114
-
115
- const replaceString = imports.join("\n");
116
- // biome-ignore lint: easier for now
117
- src = src.replace(importRule, replaceString);
118
- }
119
-
120
- // Return the transformed source
121
- return src;
122
- }
package/src/types.ts DELETED
@@ -1,8 +0,0 @@
1
- export interface PluginOptions {
2
- ignorePaths?: string[];
3
- }
4
-
5
- export interface TransformResult {
6
- code: string;
7
- map: null;
8
- }
@@ -1,107 +0,0 @@
1
- import { describe, expect, it, vi } from "vitest";
2
- import sassGlobImportPlugin from "../src";
3
-
4
- const source = `
5
- body {}
6
- @import "files/*.scss";
7
- `;
8
-
9
- describe("it correctly converts glob patterns to inline imports", () => {
10
- // biome-ignore lint: TODO
11
- const plugin: any = sassGlobImportPlugin();
12
-
13
- it("for SCSS", () => {
14
- const expected = `
15
- body {}
16
- @import "files/_file-a.scss";
17
- @import "files/_file-b.scss";
18
- `;
19
- const path = `${__dirname}/virtual-file.scss`;
20
- expect(plugin.transform(source, path)?.code).toEqual(expected);
21
- });
22
-
23
- it("for Sass", () => {
24
- const expected = `
25
- body {}
26
- @import "files/_file-a.scss"
27
- @import "files/_file-b.scss"
28
- `;
29
- const path = `${__dirname}/virtual-file.sass`;
30
- expect(plugin.transform(source, path)?.code).toEqual(expected);
31
- });
32
-
33
- it("with @use", () => {
34
- const source = `
35
- body {}
36
- @use "files/*.scss";
37
- `;
38
- const expected = `
39
- body {}
40
- @use "files/_file-a.scss";
41
- @use "files/_file-b.scss";
42
- `;
43
- const path = `${__dirname}/virtual-file.scss`;
44
- expect(plugin.transform(source, path)?.code).toEqual(expected);
45
- });
46
- });
47
-
48
- describe("it warns for invalid glob paths", () => {
49
- // biome-ignore lint: TODO
50
- const plugin: any = sassGlobImportPlugin();
51
-
52
- it("for SCSS", () => {
53
- const source = `
54
- body {}
55
- @use "foo/**/*.scss";
56
- `;
57
- const expected = `
58
- body {}
59
-
60
- `;
61
- const path = `${__dirname}/virtual-file.scss`;
62
- vi.spyOn(console, "warn");
63
- expect(plugin.transform(source, path)?.code).toEqual(expected);
64
- expect(console.warn).toHaveBeenCalledTimes(1);
65
- });
66
- });
67
-
68
- describe("it correctly converts glob patterns with static trail to namespace imports", () => {
69
- // biome-ignore lint: TODO
70
- const plugin: any = sassGlobImportPlugin();
71
-
72
- it.todo("for SCSS", () => {
73
- //TODO does this even work?
74
- const expected = `
75
- body {}
76
- @import "files/a/foo.scss";
77
- @import "files/b/foo.scss";
78
- `;
79
- const path = `${__dirname}/virtual-file.scss`;
80
- expect(plugin.transform(source, path)?.code).toEqual(expected);
81
- });
82
-
83
- it.todo("for Sass", () => {
84
- //TODO does this even work?
85
- const expected = `
86
- body {}
87
- @import "files/a/foo.scss"
88
- @import "files/b/foo.scss"
89
- `;
90
- const path = `${__dirname}/virtual-file.sass`;
91
- expect(plugin.transform(source, path)?.code).toEqual(expected);
92
- });
93
-
94
- it("with @use", () => {
95
- const source = `
96
- body {}
97
- @use "files/*/foo.scss";
98
- `;
99
- const expected = `
100
- body {}
101
- @use "files/a/foo.scss" as foo_0;
102
- @use "files/b/foo.scss" as foo_1;
103
- `;
104
- const path = `${__dirname}/virtual-file.scss`;
105
- expect(plugin.transform(source, path)?.code).toEqual(expected);
106
- });
107
- });
File without changes
File without changes
File without changes
File without changes
package/tsconfig.json DELETED
@@ -1,12 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ESNext",
4
- "module": "ESNext",
5
- "lib": ["ESNext", "DOM"],
6
- "moduleResolution": "Node",
7
- "allowSyntheticDefaultImports": true,
8
- "strict": true,
9
- "sourceMap": true
10
- },
11
- "include": ["./src"]
12
- }