poops 1.0.20 → 1.2.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/README.md +674 -138
- package/lib/copy.js +14 -18
- package/lib/markup/collections.js +247 -0
- package/lib/markup/engines/liquid.js +240 -0
- package/lib/markup/engines/nunjucks.js +261 -0
- package/lib/markup/helpers.js +170 -0
- package/lib/markup/highlight.js +77 -0
- package/lib/markup/indexer.js +154 -0
- package/lib/markup/stop-words-en.json +25 -0
- package/lib/markups.js +233 -456
- package/lib/postcss.js +127 -0
- package/lib/reactor.js +169 -0
- package/lib/scripts.js +20 -23
- package/lib/styles.js +28 -117
- package/lib/utils/helpers.js +41 -181
- package/lib/utils/log.js +64 -0
- package/package.json +38 -12
- package/poops.js +153 -158
- package/lib/utils/print-style.js +0 -72
package/lib/postcss.js
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { transform } from 'esbuild'
|
|
2
|
+
import fs from 'node:fs'
|
|
3
|
+
import {
|
|
4
|
+
pathExists,
|
|
5
|
+
mkPath,
|
|
6
|
+
insertMinSuffix,
|
|
7
|
+
buildStyleOutputFilePath,
|
|
8
|
+
fillBannerTemplate,
|
|
9
|
+
buildTime,
|
|
10
|
+
fileSize
|
|
11
|
+
} from './utils/helpers.js'
|
|
12
|
+
import log from './utils/log.js'
|
|
13
|
+
|
|
14
|
+
let postcss
|
|
15
|
+
|
|
16
|
+
async function loadPostCSS() {
|
|
17
|
+
if (postcss) return true
|
|
18
|
+
try {
|
|
19
|
+
postcss = (await import('postcss')).default
|
|
20
|
+
return true
|
|
21
|
+
} catch (err) {
|
|
22
|
+
log({ tag: 'postcss', error: true, text: 'Missing dependency. Install: npm i -D postcss' })
|
|
23
|
+
return false
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
async function resolvePlugins(plugins) {
|
|
28
|
+
if (!plugins || !plugins.length) return []
|
|
29
|
+
|
|
30
|
+
const resolved = []
|
|
31
|
+
for (const plugin of plugins) {
|
|
32
|
+
if (typeof plugin === 'string') {
|
|
33
|
+
try {
|
|
34
|
+
const mod = await import(plugin)
|
|
35
|
+
resolved.push((mod.default || mod)())
|
|
36
|
+
} catch (err) {
|
|
37
|
+
log({ tag: 'postcss', error: true, text: `Failed to load plugin: ${plugin}` })
|
|
38
|
+
throw err
|
|
39
|
+
}
|
|
40
|
+
} else if (Array.isArray(plugin)) {
|
|
41
|
+
const [name, options] = plugin
|
|
42
|
+
try {
|
|
43
|
+
const mod = await import(name)
|
|
44
|
+
resolved.push((mod.default || mod)(options))
|
|
45
|
+
} catch (err) {
|
|
46
|
+
log({ tag: 'postcss', error: true, text: `Failed to load plugin: ${name}` })
|
|
47
|
+
throw err
|
|
48
|
+
}
|
|
49
|
+
} else {
|
|
50
|
+
resolved.push(plugin)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
return resolved
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export default class PostCSS {
|
|
57
|
+
constructor(config) {
|
|
58
|
+
this.config = config
|
|
59
|
+
this.banner = config.banner ? fillBannerTemplate(config.banner) : null
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async compile() {
|
|
63
|
+
if (!this.config.postcss) return
|
|
64
|
+
if (!(await loadPostCSS())) return
|
|
65
|
+
|
|
66
|
+
this.config.postcss = Array.isArray(this.config.postcss) ? this.config.postcss : [this.config.postcss]
|
|
67
|
+
for (const entry of this.config.postcss) {
|
|
68
|
+
if (entry.in && entry.out && pathExists(entry.in)) {
|
|
69
|
+
mkPath(entry.out)
|
|
70
|
+
await this.compileEntry(entry.in, entry.out, entry.options)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async compileEntry(infilePath, outfilePath, options = {}) {
|
|
76
|
+
outfilePath = buildStyleOutputFilePath(infilePath, outfilePath)
|
|
77
|
+
|
|
78
|
+
const input = fs.readFileSync(infilePath, 'utf-8')
|
|
79
|
+
const start = performance.now()
|
|
80
|
+
|
|
81
|
+
let result
|
|
82
|
+
try {
|
|
83
|
+
const plugins = await resolvePlugins(options.plugins)
|
|
84
|
+
const processor = postcss(plugins)
|
|
85
|
+
result = await processor.process(input, {
|
|
86
|
+
from: infilePath,
|
|
87
|
+
to: outfilePath
|
|
88
|
+
})
|
|
89
|
+
} catch (err) {
|
|
90
|
+
log({ tag: 'postcss', error: true, text: 'Failed compiling:', link: outfilePath })
|
|
91
|
+
console.error(err)
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
let css = result.css
|
|
96
|
+
if (this.banner) css = this.banner + '\n' + css
|
|
97
|
+
fs.writeFileSync(outfilePath, css)
|
|
98
|
+
const end = performance.now()
|
|
99
|
+
if (!options.justMinified) log({ tag: 'postcss', text: 'Compiled:', link: outfilePath, size: fileSize(outfilePath), time: buildTime(start, end) })
|
|
100
|
+
|
|
101
|
+
const minPath = insertMinSuffix(outfilePath)
|
|
102
|
+
if (options.minify) {
|
|
103
|
+
try {
|
|
104
|
+
const minStart = !options.justMinified ? performance.now() : start
|
|
105
|
+
|
|
106
|
+
const minified = await transform(css, {
|
|
107
|
+
loader: 'css',
|
|
108
|
+
minify: true
|
|
109
|
+
})
|
|
110
|
+
|
|
111
|
+
if (this.banner) minified.code = this.banner + '\n' + minified.code
|
|
112
|
+
fs.writeFileSync(minPath, minified.code)
|
|
113
|
+
const minEnd = performance.now()
|
|
114
|
+
log({ tag: 'postcss', text: 'Compiled:', link: minPath, size: fileSize(minPath), time: buildTime(minStart, minEnd) })
|
|
115
|
+
} catch (err) {
|
|
116
|
+
log({ tag: 'postcss', error: true, text: 'Failed compiling:', link: minPath })
|
|
117
|
+
console.error(err)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (options.justMinified) {
|
|
121
|
+
fs.unlinkSync(outfilePath)
|
|
122
|
+
}
|
|
123
|
+
} else {
|
|
124
|
+
if (pathExists(minPath)) fs.unlinkSync(minPath)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
package/lib/reactor.js
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { build, transform } from 'esbuild'
|
|
2
|
+
import { deepMerge } from 'book-of-spells'
|
|
3
|
+
import {
|
|
4
|
+
pathExists,
|
|
5
|
+
mkPath,
|
|
6
|
+
insertMinSuffix,
|
|
7
|
+
pathContainsPathSegment,
|
|
8
|
+
fillBannerTemplate,
|
|
9
|
+
buildTime,
|
|
10
|
+
fileSize
|
|
11
|
+
} from './utils/helpers.js'
|
|
12
|
+
import fs from 'node:fs'
|
|
13
|
+
import path from 'node:path'
|
|
14
|
+
import { createRequire } from 'node:module'
|
|
15
|
+
import log from './utils/log.js'
|
|
16
|
+
|
|
17
|
+
export default class Reactor {
|
|
18
|
+
constructor(config) {
|
|
19
|
+
this.config = config
|
|
20
|
+
this.rendered = {}
|
|
21
|
+
this.renderedChanged = false
|
|
22
|
+
this.banner = config.banner ? fillBannerTemplate(config.banner, config.pkg) : null
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async compile() {
|
|
26
|
+
if (!this.config.reactor) return
|
|
27
|
+
this.config.reactor = Array.isArray(this.config.reactor) ? this.config.reactor : [this.config.reactor]
|
|
28
|
+
const prevRendered = this.rendered
|
|
29
|
+
this.rendered = {}
|
|
30
|
+
|
|
31
|
+
for (const entry of this.config.reactor) {
|
|
32
|
+
if (entry.component && entry.inject && pathExists(entry.component)) {
|
|
33
|
+
await this.compileEntry(entry)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
this.renderedChanged = JSON.stringify(this.rendered) !== JSON.stringify(prevRendered)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async compileEntry(entry) {
|
|
41
|
+
const { component, in: client, out, inject, options = {} } = entry
|
|
42
|
+
|
|
43
|
+
// --- Step 1: Server-side render the component ---
|
|
44
|
+
const tmpWrapper = component.replace(/(\.[^.]+)$/, `.reactor-tmp-${inject}$1`)
|
|
45
|
+
const tmpBundle = component.replace(/(\.[^.]+)$/, `.reactor-bundle-${inject}.cjs`)
|
|
46
|
+
|
|
47
|
+
const wrapperCode = `
|
|
48
|
+
import { renderToString } from 'react-dom/server';
|
|
49
|
+
import React from 'react';
|
|
50
|
+
import Component from './${path.basename(component)}';
|
|
51
|
+
export const html = renderToString(React.createElement(Component));
|
|
52
|
+
`
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
fs.writeFileSync(tmpWrapper, wrapperCode)
|
|
56
|
+
|
|
57
|
+
const renderStart = performance.now()
|
|
58
|
+
await build({
|
|
59
|
+
logLevel: 'error',
|
|
60
|
+
entryPoints: [tmpWrapper],
|
|
61
|
+
outfile: tmpBundle,
|
|
62
|
+
bundle: true,
|
|
63
|
+
platform: 'node',
|
|
64
|
+
format: 'cjs',
|
|
65
|
+
jsx: 'automatic',
|
|
66
|
+
nodePaths: this.config.includePaths
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
const bundlePath = path.resolve(tmpBundle)
|
|
70
|
+
const require = createRequire(import.meta.url)
|
|
71
|
+
delete require.cache[bundlePath]
|
|
72
|
+
const mod = require(bundlePath)
|
|
73
|
+
this.rendered[inject] = mod.html
|
|
74
|
+
const renderEnd = performance.now()
|
|
75
|
+
|
|
76
|
+
log({ tag: 'reactor', text: `Rendered: ${component} →`, link: inject, time: buildTime(renderStart, renderEnd) })
|
|
77
|
+
} catch (err) {
|
|
78
|
+
log({ tag: 'reactor', error: true, text: 'Failed rendering:', link: component })
|
|
79
|
+
console.error(err)
|
|
80
|
+
} finally {
|
|
81
|
+
if (fs.existsSync(tmpWrapper)) fs.unlinkSync(tmpWrapper)
|
|
82
|
+
if (fs.existsSync(tmpBundle)) fs.unlinkSync(tmpBundle)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// --- Step 2: Bundle the client entry for the browser ---
|
|
86
|
+
if (client && out && pathExists(client)) {
|
|
87
|
+
mkPath(out)
|
|
88
|
+
|
|
89
|
+
const opts = {
|
|
90
|
+
logLevel: 'error',
|
|
91
|
+
entryPoints: [client],
|
|
92
|
+
outfile: out,
|
|
93
|
+
bundle: true,
|
|
94
|
+
sourcemap: false,
|
|
95
|
+
minify: false,
|
|
96
|
+
format: 'iife',
|
|
97
|
+
target: 'es2019',
|
|
98
|
+
nodePaths: this.config.includePaths
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (this.banner) {
|
|
102
|
+
opts.banner = {
|
|
103
|
+
js: this.banner,
|
|
104
|
+
css: this.banner
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (options.format) opts.format = options.format
|
|
109
|
+
if (options.target) opts.target = options.target
|
|
110
|
+
if (options.nodePaths) opts.nodePaths = [...new Set([...opts.nodePaths, ...options.nodePaths])]
|
|
111
|
+
if (options.sourcemap) opts.sourcemap = options.sourcemap
|
|
112
|
+
|
|
113
|
+
const optionsClone = { ...options }
|
|
114
|
+
delete optionsClone.justMinified
|
|
115
|
+
delete optionsClone.minify
|
|
116
|
+
|
|
117
|
+
deepMerge(opts, optionsClone)
|
|
118
|
+
|
|
119
|
+
const esbuildStart = performance.now()
|
|
120
|
+
try {
|
|
121
|
+
await build(opts)
|
|
122
|
+
} catch (err) {
|
|
123
|
+
log({ tag: 'reactor', error: true, text: 'Failed compiling client:', link: out })
|
|
124
|
+
console.error(err)
|
|
125
|
+
return
|
|
126
|
+
}
|
|
127
|
+
const esbuildEnd = performance.now()
|
|
128
|
+
|
|
129
|
+
if (!options.justMinified) log({ tag: 'reactor', text: 'Compiled:', link: out, size: fileSize(out), time: buildTime(esbuildStart, esbuildEnd) })
|
|
130
|
+
if (options.sourcemap) log({ tag: 'reactor', text: 'Compiled:', link: `${out}.map` })
|
|
131
|
+
|
|
132
|
+
if (options.minify) {
|
|
133
|
+
const minPath = insertMinSuffix(out)
|
|
134
|
+
try {
|
|
135
|
+
const terserStart = performance.now()
|
|
136
|
+
const minifyResult = await transform(fs.readFileSync(out, 'utf-8'), {
|
|
137
|
+
minify: true,
|
|
138
|
+
loader: 'js'
|
|
139
|
+
})
|
|
140
|
+
const terserEnd = performance.now()
|
|
141
|
+
|
|
142
|
+
if (this.banner) minifyResult.code = this.banner + '\n' + minifyResult.code
|
|
143
|
+
fs.writeFileSync(minPath, minifyResult.code)
|
|
144
|
+
log({ tag: 'reactor', text: 'Compiled:', link: minPath, size: fileSize(minPath), time: buildTime(terserStart, terserEnd) })
|
|
145
|
+
} catch (err) {
|
|
146
|
+
log({ tag: 'reactor', error: true, text: 'Failed compiling:', link: minPath })
|
|
147
|
+
console.error(err)
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (options.justMinified) {
|
|
151
|
+
fs.unlinkSync(out)
|
|
152
|
+
}
|
|
153
|
+
} else {
|
|
154
|
+
const minPath = insertMinSuffix(out)
|
|
155
|
+
if (pathExists(minPath)) fs.unlinkSync(minPath)
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
getRendered() {
|
|
161
|
+
return this.rendered
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
belongsToReactor(file) {
|
|
165
|
+
if (!this.config.reactor) return false
|
|
166
|
+
const entries = Array.isArray(this.config.reactor) ? this.config.reactor : [this.config.reactor]
|
|
167
|
+
return entries.some(e => e.component && pathContainsPathSegment(file, path.dirname(e.component)))
|
|
168
|
+
}
|
|
169
|
+
}
|
package/lib/scripts.js
CHANGED
|
@@ -1,10 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const fs = require('node:fs')
|
|
5
|
-
const PrintStyle = require('./utils/print-style.js')
|
|
6
|
-
|
|
7
|
-
const {
|
|
1
|
+
import { build, transform } from 'esbuild'
|
|
2
|
+
import { deepMerge } from 'book-of-spells'
|
|
3
|
+
import {
|
|
8
4
|
pathExists,
|
|
9
5
|
mkPath,
|
|
10
6
|
pathForFile,
|
|
@@ -13,19 +9,20 @@ const {
|
|
|
13
9
|
fillBannerTemplate,
|
|
14
10
|
buildTime,
|
|
15
11
|
fileSize
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
|
|
12
|
+
} from './utils/helpers.js'
|
|
13
|
+
import fs from 'node:fs'
|
|
14
|
+
import log from './utils/log.js'
|
|
19
15
|
|
|
20
|
-
|
|
16
|
+
export default class Scripts {
|
|
21
17
|
constructor(config) {
|
|
22
18
|
this.config = config
|
|
23
|
-
this.banner = fillBannerTemplate(config.banner, config.pkg)
|
|
19
|
+
this.banner = config.banner ? fillBannerTemplate(config.banner, config.pkg) : null
|
|
24
20
|
}
|
|
25
21
|
|
|
26
22
|
async compile() {
|
|
27
23
|
if (!this.config.scripts) return
|
|
28
24
|
this.config.scripts = Array.isArray(this.config.scripts) ? this.config.scripts : [this.config.scripts]
|
|
25
|
+
|
|
29
26
|
for (const scriptEntry of this.config.scripts) {
|
|
30
27
|
if (scriptEntry.in && scriptEntry.out && pathExists(scriptEntry.in)) {
|
|
31
28
|
mkPath(scriptEntry.out)
|
|
@@ -34,7 +31,7 @@ module.exports = class Scripts {
|
|
|
34
31
|
}
|
|
35
32
|
}
|
|
36
33
|
|
|
37
|
-
async compileEntry(infilePath, outfilePath, options = {}) {
|
|
34
|
+
async compileEntry(infilePath, outfilePath, options = {}, tag = 'script') {
|
|
38
35
|
if (!Array.isArray(infilePath)) infilePath = [infilePath]
|
|
39
36
|
|
|
40
37
|
const opts = {
|
|
@@ -59,7 +56,7 @@ module.exports = class Scripts {
|
|
|
59
56
|
opts.outdir = outfilePath
|
|
60
57
|
} else {
|
|
61
58
|
if (infilePath.length > 1) {
|
|
62
|
-
|
|
59
|
+
log({ tag: 'error', text: 'Cannot output multiple script files to a single file. Please specify an output directory path instead.' })
|
|
63
60
|
process.exit(1)
|
|
64
61
|
}
|
|
65
62
|
opts.outfile = outfilePath
|
|
@@ -74,15 +71,15 @@ module.exports = class Scripts {
|
|
|
74
71
|
delete optionsClone.justMinified
|
|
75
72
|
delete optionsClone.minify
|
|
76
73
|
|
|
77
|
-
|
|
78
|
-
deepmerge(opts, optionsClone) // ability to pass other esbuild options `node_modules/esbuild/lib/main.d.ts`
|
|
74
|
+
deepMerge(opts, optionsClone) // ability to pass other esbuild options `node_modules/esbuild/lib/main.d.ts`
|
|
79
75
|
|
|
80
76
|
const esbuildStart = performance.now()
|
|
81
77
|
try {
|
|
82
78
|
await build(opts)
|
|
83
79
|
} catch (err) {
|
|
84
|
-
|
|
85
|
-
console.
|
|
80
|
+
log({ tag, error: true, text: 'Failed compiling:', link: outfilePath })
|
|
81
|
+
console.error(err)
|
|
82
|
+
return
|
|
86
83
|
}
|
|
87
84
|
const esbuildEnd = performance.now()
|
|
88
85
|
|
|
@@ -90,8 +87,8 @@ module.exports = class Scripts {
|
|
|
90
87
|
const newOutFilePath = buildScriptOutputFilePath(entry, outfilePath)
|
|
91
88
|
const minPath = insertMinSuffix(newOutFilePath)
|
|
92
89
|
|
|
93
|
-
if (!options.justMinified)
|
|
94
|
-
if (options.sourcemap)
|
|
90
|
+
if (!options.justMinified) log({ tag, text: 'Compiled:', link: newOutFilePath, size: fileSize(newOutFilePath), time: buildTime(esbuildStart, esbuildEnd) })
|
|
91
|
+
if (options.sourcemap) log({ tag, text: 'Compiled:', link: `${newOutFilePath}.map` })
|
|
95
92
|
|
|
96
93
|
if (options.minify) {
|
|
97
94
|
try {
|
|
@@ -104,10 +101,10 @@ module.exports = class Scripts {
|
|
|
104
101
|
|
|
105
102
|
if (this.banner) minifyResult.code = this.banner + '\n' + minifyResult.code
|
|
106
103
|
fs.writeFileSync(minPath, minifyResult.code)
|
|
107
|
-
|
|
104
|
+
log({ tag, text: 'Compiled:', link: minPath, size: fileSize(minPath), time: buildTime(terserStart, terserEnd) })
|
|
108
105
|
} catch (err) {
|
|
109
|
-
|
|
110
|
-
console.
|
|
106
|
+
log({ tag, error: true, text: 'Failed compiling:', link: minPath })
|
|
107
|
+
console.error(err)
|
|
111
108
|
}
|
|
112
109
|
|
|
113
110
|
if (options.justMinified) {
|
package/lib/styles.js
CHANGED
|
@@ -1,110 +1,21 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
const path = require('node:path')
|
|
5
|
-
const { pathToFileURL } = require('node:url')
|
|
6
|
-
const sass = require('sass')
|
|
7
|
-
const PrintStyle = require('./utils/print-style.js')
|
|
8
|
-
|
|
9
|
-
const {
|
|
1
|
+
import { transform } from 'esbuild'
|
|
2
|
+
import fs from 'node:fs'
|
|
3
|
+
import {
|
|
10
4
|
pathExists,
|
|
11
|
-
pathIsDirectory,
|
|
12
5
|
mkPath,
|
|
13
6
|
insertMinSuffix,
|
|
14
7
|
buildStyleOutputFilePath,
|
|
15
8
|
fillBannerTemplate,
|
|
16
9
|
buildTime,
|
|
17
10
|
fileSize
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
let fileExt = extensions.find(ext => fs.existsSync(`${filePath}.${ext}`))
|
|
29
|
-
if (fileExt) return `${filePath}.${fileExt}`
|
|
30
|
-
|
|
31
|
-
if (!pathParts.name.startsWith('_')) {
|
|
32
|
-
pathParts.name = `_${pathParts.name}`
|
|
33
|
-
const underscoredFilePath = path.format(pathParts)
|
|
34
|
-
fileExt = extensions.find(ext => fs.existsSync(`${underscoredFilePath}.${ext}`))
|
|
35
|
-
if (fileExt) return `${underscoredFilePath}.${fileExt}`
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
return null
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function extractMainPathFromPackageJson(packageJsonPath) {
|
|
42
|
-
if (!pathExists(packageJsonPath, 'package.json')) return null
|
|
43
|
-
|
|
44
|
-
const pkg = require(path.join(process.cwd(), packageJsonPath, 'package.json'))
|
|
45
|
-
|
|
46
|
-
const mainPath = pkg.sass || pkg.scss || pkg.style || pkg.css || pkg.main
|
|
47
|
-
if (!mainPath) return null
|
|
48
|
-
|
|
49
|
-
return mainPath
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function getPackagePath(url) {
|
|
53
|
-
const parts = path.parse(url)
|
|
54
|
-
if (!parts.dir) return null
|
|
55
|
-
const dirChunks = parts.dir.split(path.sep)
|
|
56
|
-
if (dirChunks.length === 0) return null
|
|
57
|
-
if (dirChunks[0].startsWith('@') && dirChunks.length > 1) {
|
|
58
|
-
return path.join(dirChunks[0], dirChunks[1])
|
|
59
|
-
}
|
|
60
|
-
return dirChunks[0]
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function sassPathResolver(url, resolvePath, infilePath) {
|
|
64
|
-
// check if resole path, like `node_modules` exists
|
|
65
|
-
const resolvedPath = pathToFileURL(resolvePath)
|
|
66
|
-
if (!fs.existsSync(resolvedPath.pathname)) return null
|
|
67
|
-
const importPath = path.relative(process.cwd(), path.join(resolvedPath.pathname, url))
|
|
68
|
-
|
|
69
|
-
// 1. Maybe it's a directory?
|
|
70
|
-
if (pathExists(importPath) && pathIsDirectory(importPath)) {
|
|
71
|
-
// Try to find an index file within the directory
|
|
72
|
-
const correctIndexFile = tryToFindFile(path.join(importPath, 'index'), ['sass', 'scss', 'css'])
|
|
73
|
-
if (correctIndexFile) return new URL(correctIndexFile, resolvedPath)
|
|
74
|
-
|
|
75
|
-
// package.json discovery
|
|
76
|
-
const style = extractMainPathFromPackageJson(importPath)
|
|
77
|
-
|
|
78
|
-
const stylePath = new URL(path.join(importPath, style), resolvedPath)
|
|
79
|
-
if (fs.existsSync(stylePath)) return stylePath
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
// 2. Maybe it's a file?
|
|
83
|
-
if (pathExists(importPath)) return pathToFileURL(importPath)
|
|
84
|
-
|
|
85
|
-
// 2.1 Try to find the correct file with different formats
|
|
86
|
-
const correctFile = tryToFindFile(importPath, ['sass', 'scss', 'css'])
|
|
87
|
-
if (correctFile) return new URL(correctFile, resolvedPath)
|
|
88
|
-
|
|
89
|
-
// 2.2 Maybe it's a file within a package?
|
|
90
|
-
const packagePath = getPackagePath(url)
|
|
91
|
-
if (packagePath) {
|
|
92
|
-
const packageFullPath = path.relative(process.cwd(), path.join(resolvedPath.pathname, packagePath))
|
|
93
|
-
const stylePath = extractMainPathFromPackageJson(packageFullPath)
|
|
94
|
-
|
|
95
|
-
if (stylePath) {
|
|
96
|
-
const styleDir = path.dirname(stylePath)
|
|
97
|
-
const styleFinalPath = path.join(packageFullPath, styleDir, url.replace(packagePath, ''))
|
|
98
|
-
|
|
99
|
-
const correctPackageFile = tryToFindFile(styleFinalPath, ['sass', 'scss', 'css'])
|
|
100
|
-
if (correctPackageFile) return new URL(correctPackageFile, resolvedPath)
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
return null
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
module.exports = class Styles {
|
|
11
|
+
} from './utils/helpers.js'
|
|
12
|
+
import path from 'node:path'
|
|
13
|
+
import * as sass from 'sass'
|
|
14
|
+
import log from './utils/log.js'
|
|
15
|
+
import { sassPathResolver } from 'sass-path-resolver'
|
|
16
|
+
import { sassTokenImporter } from 'sass-token-importer'
|
|
17
|
+
|
|
18
|
+
export default class Styles {
|
|
108
19
|
constructor(config) {
|
|
109
20
|
this.config = config
|
|
110
21
|
this.banner = config.banner ? fillBannerTemplate(config.banner) : null
|
|
@@ -124,19 +35,19 @@ module.exports = class Styles {
|
|
|
124
35
|
async compileEntry(infilePath, outfilePath, options = {}) {
|
|
125
36
|
const includePaths = this.config.includePaths || []
|
|
126
37
|
|
|
38
|
+
const importers = [sassPathResolver(includePaths)]
|
|
39
|
+
|
|
40
|
+
if (options.tokenPaths) {
|
|
41
|
+
const tokenOpts = {}
|
|
42
|
+
if (options.tokenOutput) tokenOpts.output = options.tokenOutput
|
|
43
|
+
if (options.resolveAliases !== undefined) tokenOpts.resolveAliases = options.resolveAliases
|
|
44
|
+
importers.push(sassTokenImporter(options.tokenPaths, tokenOpts))
|
|
45
|
+
}
|
|
46
|
+
|
|
127
47
|
const opts = {
|
|
128
48
|
sourceMap: false,
|
|
129
49
|
sourceMapIncludeSources: false,
|
|
130
|
-
importers
|
|
131
|
-
// Resolve `includePaths`.
|
|
132
|
-
findFileUrl(url) {
|
|
133
|
-
for (const includePath of includePaths) {
|
|
134
|
-
const resolvedPath = sassPathResolver(url, includePath, infilePath)
|
|
135
|
-
if (resolvedPath && pathExists(resolvedPath.pathname)) return resolvedPath
|
|
136
|
-
}
|
|
137
|
-
return null
|
|
138
|
-
}
|
|
139
|
-
}]
|
|
50
|
+
importers
|
|
140
51
|
}
|
|
141
52
|
|
|
142
53
|
if (options.sourcemap) {
|
|
@@ -151,8 +62,8 @@ module.exports = class Styles {
|
|
|
151
62
|
try {
|
|
152
63
|
compiledSass = sass.compile(infilePath, opts)
|
|
153
64
|
} catch (err) {
|
|
154
|
-
|
|
155
|
-
console.
|
|
65
|
+
log({ tag: 'style', error: true, text: 'Failed compiling:', link: outfilePath })
|
|
66
|
+
console.error(err)
|
|
156
67
|
return
|
|
157
68
|
}
|
|
158
69
|
|
|
@@ -160,12 +71,12 @@ module.exports = class Styles {
|
|
|
160
71
|
if (this.banner) compiledSass.css = this.banner + '\n' + compiledSass.css
|
|
161
72
|
fs.writeFileSync(outfilePath, compiledSass.css + mapsrc)
|
|
162
73
|
const stylesEnd = performance.now()
|
|
163
|
-
if (!options.justMinified)
|
|
74
|
+
if (!options.justMinified) log({ tag: 'style', text: 'Compiled:', link: outfilePath, size: fileSize(outfilePath), time: buildTime(stylesStart, stylesEnd) })
|
|
164
75
|
|
|
165
76
|
if (compiledSass.sourceMap) {
|
|
166
77
|
if (this.banner) compiledSass.sourceMap.mappings = ';' + compiledSass.sourceMap.mappings
|
|
167
78
|
fs.writeFileSync(`${outfilePath}.map`, JSON.stringify(compiledSass.sourceMap))
|
|
168
|
-
|
|
79
|
+
log({ tag: 'style', text: 'Compiled:', link: `${outfilePath}.map` })
|
|
169
80
|
}
|
|
170
81
|
|
|
171
82
|
const minPath = insertMinSuffix(outfilePath)
|
|
@@ -181,10 +92,10 @@ module.exports = class Styles {
|
|
|
181
92
|
if (this.banner) minified.code = this.banner + '\n' + minified.code
|
|
182
93
|
fs.writeFileSync(minPath, minified.code)
|
|
183
94
|
const stylesMinEnd = performance.now()
|
|
184
|
-
|
|
95
|
+
log({ tag: 'style', text: 'Compiled:', link: minPath, size: fileSize(minPath), time: buildTime(stylesMinStart, stylesMinEnd) })
|
|
185
96
|
} catch (err) {
|
|
186
|
-
|
|
187
|
-
console.
|
|
97
|
+
log({ tag: 'style', error: true, text: 'Failed compiling:', link: minPath })
|
|
98
|
+
console.error(err)
|
|
188
99
|
}
|
|
189
100
|
|
|
190
101
|
if (options.justMinified) {
|