poops 1.9.4 → 1.9.5
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 +4 -4
- package/lib/copy.js +1 -1
- package/lib/scripts.js +15 -2
- package/lib/styles.js +17 -2
- package/lib/utils/helpers.js +30 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -216,8 +216,8 @@ You can freely remove the properties that you don't need. For example, if you do
|
|
|
216
216
|
|
|
217
217
|
Scripts are bundled with [esbuild](https://esbuild.github.io/). Supports `.js`, `.ts`, `.jsx`, and `.tsx` files out of the box — including React and other JSX frameworks. You can specify multiple scripts to bundle. Each script has the following properties:
|
|
218
218
|
|
|
219
|
-
- `in` - the input path, can be a file path, an array of file paths, or a glob pattern (e.g. `"src/js/*.js"`). Globs must use `/` separators (even on Windows)
|
|
220
|
-
- `out` - the output path, can be a directory or a file path. With multiple inputs it must be a directory — entry points from different directories nest their output under the common ancestor (esbuild's `outbase`)
|
|
219
|
+
- `in` - the input path, can be a file path, an array of file paths, or a glob pattern (e.g. `"src/js/*.js"`, `"src/elements/*/index.{js,mjs,cjs,jsx,ts,tsx}"`). Globs must use `/` separators (even on Windows)
|
|
220
|
+
- `out` - the output path, can be a directory or a file path. With multiple inputs it must be a directory — entry points from different directories nest their output under the common ancestor (esbuild's `outbase`). A **glob-matched** `index.*` is named after its directory instead, placed relative to the glob's static prefix: `"src/elements/*/index.js"` gives `<out>/accordion.js`, while `"src/*/accordion/index.js"` keeps the differing segment as `<out>/blocks/accordion.js`. A literal `in: "src/index.js"` keeps its own basename
|
|
221
221
|
- `options` - the options for the bundler. You can apply most of the esbuild options that are not in conflict with Poops. See [esbuild's options](https://esbuild.github.io/api/#build-api) for more info.
|
|
222
222
|
|
|
223
223
|
**Options:**
|
|
@@ -350,8 +350,8 @@ Poops does not need `react` or `react-dom` as its own dependency — they are re
|
|
|
350
350
|
|
|
351
351
|
Styles are bundled with [Dart Sass](https://sass-lang.com/dart-sass). You can specify multiple styles to bundle. Each style has the following properties:
|
|
352
352
|
|
|
353
|
-
- `in` - the input path, can be a file path, an array of file paths, or a glob pattern (e.g. `"src/scss/*.scss"`). Globs must use `/` separators (even on Windows) and skip Sass partials (`_*.scss`). Each matched file is compiled separately
|
|
354
|
-
- `out` - the output path, can be a directory or a file path. With multiple inputs it must be a directory — each input compiles to `<out>/<basename>.css`, so inputs sharing a basename (e.g. `a/main.scss` and `b/main.scss`) will overwrite each other
|
|
353
|
+
- `in` - the input path, can be a file path, an array of file paths, or a glob pattern (e.g. `"src/scss/*.scss"`, `"src/elements/*/index.{scss,sass,css}"`). Globs must use `/` separators (even on Windows) and skip Sass partials (`_*.scss`). Each matched file is compiled separately
|
|
354
|
+
- `out` - the output path, can be a directory or a file path. With multiple inputs it must be a directory — each input compiles to `<out>/<basename>.css`, so inputs sharing a basename (e.g. `a/main.scss` and `b/main.scss`) will overwrite each other. A **glob-matched** `index.*` is named after its directory instead, placed relative to the glob's static prefix: `"src/elements/*/index.scss"` gives `<out>/accordion.css`, while `"src/*/accordion/index.scss"` keeps the differing segment as `<out>/blocks/accordion.css`. A literal `in: "src/scss/index.scss"` keeps its own basename
|
|
355
355
|
- `options` - the options for the bundler.
|
|
356
356
|
|
|
357
357
|
**Options:**
|
package/lib/copy.js
CHANGED
|
@@ -34,7 +34,7 @@ export default class Copy {
|
|
|
34
34
|
|
|
35
35
|
for (const inEntry of inEntries) {
|
|
36
36
|
const hasGlobMagic = typeof hasMagic === 'function'
|
|
37
|
-
? hasMagic(inEntry)
|
|
37
|
+
? hasMagic(inEntry, { magicalBraces: true })
|
|
38
38
|
: ['*', '?', '[', ']', '{', '}', '(', ')', '!'].some((ch) => inEntry.includes(ch))
|
|
39
39
|
let matches = []
|
|
40
40
|
try {
|
package/lib/scripts.js
CHANGED
|
@@ -5,6 +5,9 @@ import {
|
|
|
5
5
|
pathExists,
|
|
6
6
|
mkPath,
|
|
7
7
|
pathForFile,
|
|
8
|
+
isIndexEntry,
|
|
9
|
+
globBase,
|
|
10
|
+
indexEntryOut,
|
|
8
11
|
fillBannerTemplate,
|
|
9
12
|
buildTime,
|
|
10
13
|
fileSize
|
|
@@ -29,14 +32,21 @@ export default class Scripts {
|
|
|
29
32
|
const entryPoints = []
|
|
30
33
|
let missing = false
|
|
31
34
|
for (const entry of configured) {
|
|
32
|
-
if (hasMagic(entry)) {
|
|
35
|
+
if (hasMagic(entry, { magicalBraces: true })) {
|
|
33
36
|
// Globs must use `/` even on Windows; sort for deterministic build order
|
|
34
37
|
const matches = globSync(entry, { posix: true }).sort()
|
|
35
38
|
if (!matches.length) {
|
|
36
39
|
log({ tag: 'script', error: true, text: 'Entry does not exist:', link: entry })
|
|
37
40
|
missing = true
|
|
38
41
|
}
|
|
39
|
-
|
|
42
|
+
// A glob-matched `index.js` is named after its directory, relative to
|
|
43
|
+
// the glob's static prefix — otherwise every component collides on
|
|
44
|
+
// `index.js` or nests a level deep under esbuild's outbase. Literal
|
|
45
|
+
// entries keep their basename — see `isIndexEntry`.
|
|
46
|
+
const base = globBase(entry)
|
|
47
|
+
entryPoints.push(...matches.map(match => (
|
|
48
|
+
isIndexEntry(match) ? { in: match, out: indexEntryOut(match, base) } : match
|
|
49
|
+
)))
|
|
40
50
|
} else if (!pathExists(entry)) {
|
|
41
51
|
log({ tag: 'script', error: true, text: 'Entry does not exist:', link: entry })
|
|
42
52
|
missing = true
|
|
@@ -78,6 +88,9 @@ export default class Scripts {
|
|
|
78
88
|
log({ tag: 'error', text: 'Cannot output multiple script files to a single file. Please specify an output directory path instead.' })
|
|
79
89
|
process.exit(1)
|
|
80
90
|
}
|
|
91
|
+
// An explicit `outfile` names the output itself, so drop any `{ in, out }`
|
|
92
|
+
// wrapper the glob resolver added — esbuild rejects the pair.
|
|
93
|
+
opts.entryPoints = infilePath.map(entry => entry.in || entry)
|
|
81
94
|
opts.outfile = outfilePath
|
|
82
95
|
}
|
|
83
96
|
|
package/lib/styles.js
CHANGED
|
@@ -5,6 +5,9 @@ import {
|
|
|
5
5
|
mkPath,
|
|
6
6
|
pathForFile,
|
|
7
7
|
buildStyleOutputFilePath,
|
|
8
|
+
isIndexEntry,
|
|
9
|
+
globBase,
|
|
10
|
+
indexEntryOut,
|
|
8
11
|
fillBannerTemplate,
|
|
9
12
|
buildTime,
|
|
10
13
|
fileSize
|
|
@@ -30,9 +33,10 @@ export default class Styles {
|
|
|
30
33
|
// `in` may be an array of entry points and/or globs — same resolution as scripts
|
|
31
34
|
const configured = Array.isArray(styleEntry.in) ? styleEntry.in : [styleEntry.in]
|
|
32
35
|
const entryPoints = []
|
|
36
|
+
const indexEntries = new Map()
|
|
33
37
|
let missing = false
|
|
34
38
|
for (const entry of configured) {
|
|
35
|
-
if (hasMagic(entry)) {
|
|
39
|
+
if (hasMagic(entry, { magicalBraces: true })) {
|
|
36
40
|
// Globs must use `/` even on Windows; sort for deterministic build order.
|
|
37
41
|
// Skip sass partials (_*.scss) — they are imports, not entry points.
|
|
38
42
|
const matches = globSync(entry, { posix: true }).filter(match => !path.basename(match).startsWith('_')).sort()
|
|
@@ -40,6 +44,13 @@ export default class Styles {
|
|
|
40
44
|
log({ tag: 'style', error: true, text: 'Entry does not exist:', link: entry })
|
|
41
45
|
missing = true
|
|
42
46
|
}
|
|
47
|
+
// A glob-matched `index.scss` is named after its directory, relative
|
|
48
|
+
// to the glob's static prefix — otherwise every component collides on
|
|
49
|
+
// `index.css`. Literal entries keep their basename — see `isIndexEntry`.
|
|
50
|
+
const base = globBase(entry)
|
|
51
|
+
for (const match of matches) {
|
|
52
|
+
if (isIndexEntry(match)) indexEntries.set(match, indexEntryOut(match, base))
|
|
53
|
+
}
|
|
43
54
|
entryPoints.push(...matches)
|
|
44
55
|
} else if (!pathExists(entry)) {
|
|
45
56
|
log({ tag: 'style', error: true, text: 'Entry does not exist:', link: entry })
|
|
@@ -54,7 +65,11 @@ export default class Styles {
|
|
|
54
65
|
process.exit(1)
|
|
55
66
|
}
|
|
56
67
|
for (const entryPoint of entryPoints) {
|
|
57
|
-
|
|
68
|
+
// Resolve the index rename here, where `out` is still known to be a dir
|
|
69
|
+
const out = indexEntries.has(entryPoint) && !pathForFile(styleEntry.out)
|
|
70
|
+
? path.join(styleEntry.out, `${indexEntries.get(entryPoint)}.css`)
|
|
71
|
+
: styleEntry.out
|
|
72
|
+
await this.compileEntry(entryPoint, out, styleEntry.options)
|
|
58
73
|
}
|
|
59
74
|
}
|
|
60
75
|
}
|
package/lib/utils/helpers.js
CHANGED
|
@@ -34,10 +34,38 @@ export function insertMinSuffix(filePath) {
|
|
|
34
34
|
return path.join(path.dirname(filePath), `${name}.min${ext}`)
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
// `index` is a directory entry point — name the output after the directory, so
|
|
38
|
+
// `src/elements/accordion/index.scss` outputs `accordion.css`. Only applied to
|
|
39
|
+
// glob matches: a literal `in: 'src/index.js'` keeps its own basename, since
|
|
40
|
+
// renaming it to `src.js` would silently move an explicitly named output.
|
|
41
|
+
export function isIndexEntry(inputPath) {
|
|
42
|
+
const { dir, name } = path.parse(inputPath)
|
|
43
|
+
return name === 'index' && !!dir && dir !== '.' && dir !== '..'
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// The static prefix of a glob — everything before its first magic segment.
|
|
47
|
+
// `src/elements/*/index.ts` → `src/elements`, `src/*/accordion/index.ts` → `src`.
|
|
48
|
+
// Derived from the pattern alone, so it doesn't shift with what happened to match.
|
|
49
|
+
export function globBase(pattern) {
|
|
50
|
+
const parts = toPosix(pattern).split('/')
|
|
51
|
+
const magicAt = parts.findIndex(part => hasMagic(part, { magicalBraces: true }))
|
|
52
|
+
return parts.slice(0, magicAt === -1 ? -1 : magicAt).join('/')
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Extension-less output path for a glob-matched `index.*`: the directory name,
|
|
56
|
+
// keeping whatever nesting the glob's static prefix doesn't already account for.
|
|
57
|
+
// A glob spanning several groups (`src/*/accordion/index.ts`) therefore stays
|
|
58
|
+
// apart as `blocks/accordion` and `elements/accordion` instead of colliding.
|
|
59
|
+
export function indexEntryOut(inputPath, base) {
|
|
60
|
+
const dir = path.posix.dirname(toPosix(inputPath))
|
|
61
|
+
const rel = base ? path.posix.relative(base, dir) : dir
|
|
62
|
+
return rel || path.posix.basename(dir)
|
|
63
|
+
}
|
|
64
|
+
|
|
37
65
|
export function buildStyleOutputFilePath(inputPath, outputPath) {
|
|
38
66
|
if (pathForFile(outputPath)) return outputPath
|
|
39
67
|
const { name } = path.parse(inputPath)
|
|
40
|
-
return path.join(
|
|
68
|
+
return path.join(outputPath, `${name}.css`)
|
|
41
69
|
}
|
|
42
70
|
|
|
43
71
|
export function fillBannerTemplate(template, packagesPath) {
|
|
@@ -185,7 +213,7 @@ export function pathContainsPathSegment(filePath, segment) {
|
|
|
185
213
|
// Watcher paths arrive with native separators, config segments with `/`
|
|
186
214
|
filePath = toPosix(filePath)
|
|
187
215
|
segment = stripDirNavSegments(segment)
|
|
188
|
-
if (hasMagic(segment)) {
|
|
216
|
+
if (hasMagic(segment, { magicalBraces: true })) {
|
|
189
217
|
segment = convertGlobToRegex(segment)
|
|
190
218
|
if (!segment) return false
|
|
191
219
|
return segment.test(filePath)
|