Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
eklingen committed Apr 23, 2024
1 parent 4e1a6d0 commit 0acf2fd
Show file tree
Hide file tree
Showing 12 changed files with 275 additions and 0 deletions.
11 changes: 11 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

root = true

[*]
charset = utf-8
end_of_line = lf
indent_size = 2
indent_style = space
insert_final_newline = true
trim_trailing_whitespace = true
max_line_length = 160
15 changes: 15 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@

# OSX Metadata files
.DS_Store

# Node modules
node_modules/

# Yarn v3 ignore list for Not-ZeroInstalls
.yarn/*
!.yarn/patches
!.yarn/releases
!.yarn/plugins
!.yarn/sdks
!.yarn/versions
yarn-error.log
1 change: 1 addition & 0 deletions .node-version
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
20.12.2
7 changes: 7 additions & 0 deletions .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
compressionLevel: 0

defaultSemverRangePrefix: ""

enableGlobalCache: true

nodeLinker: node-modules
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@

# Changelog

v1.0.0 - Initial release
93 changes: 93 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@

# Glob importers

Plugins to add globbing support to import calls and/or statements in various environments like Sass, Webpack and ESBuild.

## Usage

### For `sass` / `sass-embedded` (with `.scss` syntax)

Add the plugin to the `plugins` field of your `sass.config.js`:

```javascript
import { sassPlugin as globImporter } from '@eklingen/glob-importers'
plugins: [globImporter()],
```

This will enable the following:

```scss
@import '../../components/**/*';
```

Will get expanded to, for example (depending on the glob and files present):

```scss
@import '../../components/button/button.scss';
@import '../../components/link/link.scss';
@import '../../components/vide/video.scss';
```

### For `esbuild`

Add the plugin to the `plugins` field of your `esbuild.config.js`:

```javascript
import { esbuildPlugin as globImporter } from '@eklingen/glob-importers'
plugins: [globImporter()],
```

This will enable the following:

```javascript
import('components/**/*.js')
```

Will get expanded to, for example (depending on the glob and files present):

```javascript
import('components/button/button.js')
import('components/link/link.js')
import('components/video/video.js')
```

### For `webpack`

Add an extra prority rule to the rules in `webpack.config.js`:

```javascript
import { path as resolvePath } from 'node:path';
const globImporter = join(process.cwd(), 'node_modules/@eklingen/glob-importers/importers/webpack.js'); // This path has to resolve, since webpack loads it dynamically.
...
{
module: {
rules: {
/* NOTE: Change `test`, `include` and `exclude` to taste. Here sourcePath is the scripts root. The plugin tries to resolve the globs on the include paths. */
{ enforce: 'pre', test: /\.((j|t)sx?)$/, include: resolvePath(__dirname, sourcePath), exclude: /node_modules/, loader: globImporter, options: { test: '(import|require)', delimiter: '\n' } },
...
}
}
}
```

This will enable the following:

```javascript
import('components/**/*.js')
```

Will get expanded to, for example (depending on the glob and files present):

```javascript
import('components/button/button.js')
import('components/link/link.js')
import('components/video/video.js')
```

## Dependencies

This package requires ["fast-glob"](https://www.npmjs.com/package/fast-glob).

---

Copyright (c) 2024 Elco Klingen. MIT License.
26 changes: 26 additions & 0 deletions importers/esbuild.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import glob from 'fast-glob'

const onResolveHandler = ({ path, namespace, resolveDir, kind }) => {
if (resolveDir === '' || namespace !== 'file' || (kind !== 'import-statement' && kind !== 'require-call' && kind !== 'dynamic-import' && kind !== 'require-resolve' && kind !== 'import-rule') || !glob.isDynamicPattern(path)) {
return
}

return { path, namespace: 'import-glob', pluginData: { resolveDir } }
}

const onLoadHandler = async args => {
const files = (await glob(args.path, { cwd: args.pluginData.resolveDir })).sort().sort((a, b) => a.localeCompare(b))
const contents = files.map(module => `import('${module}');\n`).join('')

return { contents, resolveDir: args.pluginData.resolveDir }
}

export default function globImporter() {
return {
name: 'esbuild:glob-importer-plugin',
setup: builder => {
builder.onResolve({ filter: /\.((j|t)sx?)$/ }, args => onResolveHandler(args))
builder.onLoad({ filter: /\.((j|t)sx?)$/, namespace: 'import-glob' }, args => onLoadHandler(args))
},
}
}
25 changes: 25 additions & 0 deletions importers/sass.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { join, resolve as resolvePath } from 'node:path'

import glob from 'fast-glob'

const PREFIX = 'sass-glob-importer://'

export default function globImporter(rootUrl = '') {
rootUrl = resolvePath(rootUrl)

return {
canonicalize: async (url = '', options = { fromImport: false, containgUrl: {} }) => {
url = join(rootUrl, url).replace(PREFIX, '')

return options.fromImport && glob.isDynamicPattern(url) ? new URL(url, PREFIX) : null
},

load: async (canonicalUrl) => {
const pathname = canonicalUrl.pathname.endsWith('.scss') ? canonicalUrl.pathname : canonicalUrl.pathname + '.scss'
const pathnames = [...(await glob(pathname))].sort((a, b) => a.localeCompare(b)).map(path => `@import '${path.replace(PREFIX, 'file:///')}'; `)
const contents = pathnames.join('').trim()

return { contents, syntax: 'scss' }
},
}
}
33 changes: 33 additions & 0 deletions importers/webpack.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import glob from 'fast-glob'

export default function globImporter(source = '') {
const cwd = this.context

function expandGlob(result) {
if (!result) {
return
}

const [match, quote, content] = result

if (!glob.isDynamicPattern(content)) {
return
}

const pre = result.input.slice(0, result.index)
const post = result.input.slice(result.index + match.length)
const results = [...glob.sync(content, { cwd })].sort((a, b) => a.localeCompare(b))

return results.map(filename => `${pre}${quote}${filename}${quote}${post}`).join('\n')
}

function expandLine(line, payload) {
if (!(payload && payload.trim())) {
return line
}

return expandGlob(/(['"])(.*?)\1/.exec(line)) || line
}

return source.replace(/^.*\b(import|require)\b(.*)$/gm, expandLine)
}
9 changes: 9 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { default as esbuildImporter } from './importers/esbuild.js'
import { default as sassImporter } from './importers/sass.js'
import { default as webpackImporter } from './importers/webpack.js'

export const esbuildPlugin = esbuildImporter
export const sassPlugin = sassImporter
export const webpackPlugin = webpackImporter

export default { esbuildPlugin, sassPlugin, webpackPlugin }
38 changes: 38 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
{
"name": "@eklingen/glob-importers",
"version": "0.0.99",
"description": "Plugins to add globbing support to import statements in various environments like Sass, Webpack and ESBuild.",
"repository": {
"type": "git",
"url": "git+https://github.com/eklingen/glob-importers.git"
},
"publishConfig": {
"access": "public"
},
"author": "Elco Klingen",
"license": "MIT",
"homepage": "https://github.com/eklingen/glob-importers#readme",
"bugs": {
"url": "https://github.com/eklingen/glob-importers/issues"
},
"keywords": [
"glob",
"globbing",
"import",
"imports",
"sass",
"scss",
"webpack",
"esbuild"
],
"main": "index.js",
"files": [
"index.js",
"CHANGELOG.md"
],
"type": "module",
"packageManager": "yarn@4.1.1",
"dependencies": {
"fast-glob": "^3.3.2"
}
}
13 changes: 13 additions & 0 deletions prettier.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export default {
editorconfig: true,
printWidth: 4096,
semi: false,
singleQuote: true,
trailingComma: 'es5',
bracketSpacing: true,
bracketSameLine: false,
arrowParens: 'avoid',
singleAttributePerLine: false,
requirePragma: false,
insertPragma: false,
}

0 comments on commit 0acf2fd

Please sign in to comment.