poops 1.0.1 → 1.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 +67 -11
- package/{dist → example/dist}/css/styles.css +1 -1
- package/example/dist/css/styles.css.map +1 -0
- package/{dist → example/dist}/css/styles.min.css +1 -1
- package/{dist → example/dist}/js/scripts.js +3 -3
- package/{dist → example/dist}/js/scripts.js.map +1 -1
- package/{dist → example/dist}/js/scripts.min.js +1 -1
- package/example/src/markup/_layouts/layout.html +85 -0
- package/example/src/markup/_partials/header.html +4 -0
- package/example/src/markup/index.html +5 -0
- package/index.html +11 -6
- package/lib/markups.js +149 -0
- package/lib/scripts.js +116 -0
- package/lib/styles.js +140 -0
- package/lib/utils/helpers.js +81 -0
- package/lib/utils/print-style.js +72 -0
- package/package.json +4 -2
- package/poops.js +54 -385
- package/poops.json +17 -5
- package/dist/css/styles.css.map +0 -1
- /package/{src → example/src}/js/main.ts +0 -0
- /package/{src → example/src}/js/scripts/utils.ts +0 -0
- /package/{src → example/src}/scss/index.scss +0 -0
- /package/{src → example/src}/scss/style/index.scss +0 -0
package/lib/markups.js
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
const helpers = require('./utils/helpers.js')
|
|
2
|
+
const fs = require('node:fs')
|
|
3
|
+
const glob = require('glob')
|
|
4
|
+
const nunjucks = require('nunjucks')
|
|
5
|
+
const path = require('node:path')
|
|
6
|
+
const PrintStyle = require('./utils/print-style.js')
|
|
7
|
+
|
|
8
|
+
const { pathExists, pathIsDirectory } = helpers
|
|
9
|
+
const pstyle = new PrintStyle()
|
|
10
|
+
|
|
11
|
+
class RelativeLoader extends nunjucks.Loader {
|
|
12
|
+
constructor(templatesDir, includePaths) {
|
|
13
|
+
super()
|
|
14
|
+
this.templatesDir = templatesDir
|
|
15
|
+
this.includePaths = includePaths || []
|
|
16
|
+
this.includePaths.push('_*')
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
getSource(name) {
|
|
20
|
+
let fullPath = name
|
|
21
|
+
if (!fs.existsSync(name)) {
|
|
22
|
+
let pattern = `**/${name}`
|
|
23
|
+
if (this.includePaths) {
|
|
24
|
+
pattern = `{${this.includePaths.join(',')}}/${pattern}`
|
|
25
|
+
}
|
|
26
|
+
fullPath = glob.sync(path.join(this.templatesDir, pattern))[0]
|
|
27
|
+
}
|
|
28
|
+
if (!fs.existsSync(fullPath)) {
|
|
29
|
+
throw new Error(`Template not found: ${name}`)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// TODO: Here we can interpret header yaml and pass it as a context, just like Jekyll does
|
|
33
|
+
const source = fs.readFileSync(fullPath, 'utf-8')
|
|
34
|
+
return { src: source, path: fullPath, noCache: true }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
resolve(from, to) {
|
|
38
|
+
return path.resolve(path.dirname(from), to)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
module.exports = class Markups {
|
|
43
|
+
constructor(config) {
|
|
44
|
+
this.config = config
|
|
45
|
+
|
|
46
|
+
if (!this.config.markup || !this.config.markup.in) return
|
|
47
|
+
|
|
48
|
+
this.nunjucksEnv = new nunjucks.Environment(new RelativeLoader(path.join(process.cwd(), config.markup.in), config.markup.includePaths), {
|
|
49
|
+
autoescape: true,
|
|
50
|
+
watch: false,
|
|
51
|
+
noCache: true
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
const pkgPath = path.join(process.cwd(), 'package.json')
|
|
55
|
+
|
|
56
|
+
if (fs.existsSync(pkgPath)) {
|
|
57
|
+
const pkg = require(pkgPath)
|
|
58
|
+
this.nunjucksEnv.addGlobal('package', pkg)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (this.config.markup.site) {
|
|
62
|
+
this.nunjucksEnv.addGlobal('site', this.config.markup.site)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (this.config.markup.data) {
|
|
66
|
+
for (const dataFile of this.config.markup.data) {
|
|
67
|
+
const data = require(path.join(process.cwd(), dataFile))
|
|
68
|
+
// TODO: should support YAML too. There should be a way to define the data file per template through front matter
|
|
69
|
+
this.nunjucksEnv.addGlobal(path.basename(dataFile, path.extname(dataFile)).replace(/[.\-\s]/g, '_'), data)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
this.payload = {} // TODO: still not used. This is where we can store data from data files. Can be used to pass front matter data or even a way to pass data from files to templates
|
|
74
|
+
|
|
75
|
+
if (!this.config.markup.out) {
|
|
76
|
+
this.config.markup.out = process.cwd()
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
generateMarkupGlobPattern(excludes) {
|
|
81
|
+
let markupDefaultExcludes = ['node_modules', '.git', '.svn', '.hg']
|
|
82
|
+
|
|
83
|
+
if (excludes) {
|
|
84
|
+
markupDefaultExcludes.push(...excludes)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (this.config.includePaths) {
|
|
88
|
+
markupDefaultExcludes.push(...this.config.includePaths)
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
markupDefaultExcludes.push('_*')
|
|
92
|
+
|
|
93
|
+
markupDefaultExcludes = [...new Set(markupDefaultExcludes)] // Remove duplicates
|
|
94
|
+
|
|
95
|
+
return `!(${markupDefaultExcludes.join('|')})/**/*.+(html|njk)`
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
compileEntry(templateName, context) {
|
|
99
|
+
const env = this.nunjucksEnv
|
|
100
|
+
return new Promise((resolve, reject) => {
|
|
101
|
+
env.getTemplate(templateName).render(context, (error, result) => {
|
|
102
|
+
if (!error) {
|
|
103
|
+
resolve(result)
|
|
104
|
+
} else {
|
|
105
|
+
reject(error)
|
|
106
|
+
}
|
|
107
|
+
})
|
|
108
|
+
})
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
compile() {
|
|
112
|
+
// TODO: should support multiple markup paths, for loop here!
|
|
113
|
+
if (!this.config.markup || !this.config.markup.in) return
|
|
114
|
+
|
|
115
|
+
const markupIn = path.join(process.cwd(), this.config.markup.in)
|
|
116
|
+
|
|
117
|
+
if (!pathExists(markupIn)) {
|
|
118
|
+
console.log(`${pstyle.redBright + pstyle.bold}[error]${pstyle.reset} Markup path does not exist: ${pstyle.dim}${markupIn}${pstyle.reset}`)
|
|
119
|
+
return
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (pathIsDirectory(markupIn)) {
|
|
123
|
+
const markupFiles = [...glob.sync(path.join(markupIn, this.generateMarkupGlobPattern(this.config.markup.includePaths))), ...glob.sync(path.join(markupIn, '*.+(html|njk)'))]
|
|
124
|
+
const compilePromises = []
|
|
125
|
+
markupFiles.forEach((file) => {
|
|
126
|
+
const markupOut = path.join(process.cwd(), path.relative(this.config.markup.in, file))
|
|
127
|
+
const markupOutDir = path.dirname(markupOut)
|
|
128
|
+
|
|
129
|
+
if (!pathExists(markupOutDir)) {
|
|
130
|
+
fs.mkdirSync(markupOutDir, { recursive: true })
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const compilePromise = this.compileEntry(file, this.payload).then((result) => {
|
|
134
|
+
fs.writeFileSync(markupOut, result)
|
|
135
|
+
})
|
|
136
|
+
compilePromises.push(compilePromise)
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
Promise.all(compilePromises).then(() => {
|
|
140
|
+
console.log(`${pstyle.cyanBright + pstyle.bold}[markup]${pstyle.reset} ${pstyle.dim}Compiled: ${pstyle.reset}${markupFiles.length} file${markupFiles.length > 1 ? 's' : ''} into ${pstyle.italic + pstyle.underline}${this.config.markup.out}${pstyle.reset}`)
|
|
141
|
+
})
|
|
142
|
+
} else {
|
|
143
|
+
this.compileEntry(markupIn, this.pkg).then((result) => {
|
|
144
|
+
fs.writeFileSync(path.join(process.cwd(), this.config.markup.out, path.basename(markupIn)), result)
|
|
145
|
+
console.log(`${pstyle.cyanBright + pstyle.bold}[markup]${pstyle.reset} ${pstyle.dim}Compiled:${pstyle.reset} ${pstyle.italic + pstyle.underline}${path.relative(process.cwd(), path.join(process.cwd(), this.config.markup.out, path.basename(markupIn)))}${pstyle.reset}`)
|
|
146
|
+
})
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
package/lib/scripts.js
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
const { build } = require('esbuild')
|
|
2
|
+
const deepmerge = require('deepmerge')
|
|
3
|
+
const helpers = require('./utils/helpers.js')
|
|
4
|
+
const fs = require('node:fs')
|
|
5
|
+
const PrintStyle = require('./utils/print-style.js')
|
|
6
|
+
const Terser = require('terser')
|
|
7
|
+
|
|
8
|
+
const {
|
|
9
|
+
pathExists,
|
|
10
|
+
mkPath,
|
|
11
|
+
pathForFile,
|
|
12
|
+
insertMinSuffix,
|
|
13
|
+
buildScriptOutputFilePath,
|
|
14
|
+
fillBannerTemplate,
|
|
15
|
+
buildTime,
|
|
16
|
+
fileSize
|
|
17
|
+
} = helpers
|
|
18
|
+
|
|
19
|
+
const pstyle = new PrintStyle()
|
|
20
|
+
|
|
21
|
+
module.exports = class Scripts {
|
|
22
|
+
constructor(config) {
|
|
23
|
+
this.config = config
|
|
24
|
+
this.banner = fillBannerTemplate(config.banner, config.pkg)
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
compile() {
|
|
28
|
+
if (!this.config.scripts) return
|
|
29
|
+
this.config.scripts = Array.isArray(this.config.scripts) ? this.config.scripts : [this.config.scripts]
|
|
30
|
+
for (const scriptEntry of this.config.scripts) {
|
|
31
|
+
if (scriptEntry.in && scriptEntry.out && pathExists(scriptEntry.in)) {
|
|
32
|
+
mkPath(scriptEntry.out)
|
|
33
|
+
this.compileEntry(scriptEntry.in, scriptEntry.out, scriptEntry.options)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
compileEntry(infilePath, outfilePath, options = {}) {
|
|
39
|
+
if (!Array.isArray(infilePath)) infilePath = [infilePath]
|
|
40
|
+
|
|
41
|
+
const opts = {
|
|
42
|
+
logLevel: 'error',
|
|
43
|
+
entryPoints: infilePath,
|
|
44
|
+
bundle: true,
|
|
45
|
+
sourcemap: false,
|
|
46
|
+
minify: false,
|
|
47
|
+
format: 'iife',
|
|
48
|
+
target: 'es2019',
|
|
49
|
+
nodePaths: this.config.includePaths // Resolve `includePaths`
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const terserOpts = {
|
|
53
|
+
mangle: false
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (this.banner) {
|
|
57
|
+
opts.banner = {
|
|
58
|
+
js: this.banner,
|
|
59
|
+
css: this.banner
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (!pathForFile(outfilePath)) {
|
|
64
|
+
opts.outdir = outfilePath
|
|
65
|
+
} else {
|
|
66
|
+
if (infilePath.length > 1) {
|
|
67
|
+
console.log(`${pstyle.redBright + pstyle.bold}[error]${pstyle.reset} Cannot output multiple ${pstyle.bold + pstyle.underline}script${pstyle.reset} files to a single file. Please specify an output directory path instead.`)
|
|
68
|
+
process.exit(1)
|
|
69
|
+
}
|
|
70
|
+
opts.outfile = outfilePath
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (options.format) opts.format = options.format
|
|
74
|
+
if (options.target) opts.target = options.target
|
|
75
|
+
if (options.nodePaths) opts.nodePaths = [...new Set([...opts.nodePaths, ...options.nodePaths])]
|
|
76
|
+
if (options.sourcemap) opts.sourcemap = options.sourcemap
|
|
77
|
+
|
|
78
|
+
if (options.mangle) terserOpts.mangle = options.mangle
|
|
79
|
+
|
|
80
|
+
delete options.mangle
|
|
81
|
+
deepmerge(opts, options) // ability to pass other esbuild options `node_modules/esbuild/lib/main.d.ts`
|
|
82
|
+
|
|
83
|
+
// TODO: Actually loop the build process for each entry point!!!
|
|
84
|
+
const esbuildStart = performance.now()
|
|
85
|
+
build(opts).then(() => {
|
|
86
|
+
const esbuildEnd = performance.now()
|
|
87
|
+
for (const entry of infilePath) {
|
|
88
|
+
const newOutFilePath = buildScriptOutputFilePath(entry, outfilePath)
|
|
89
|
+
const minPath = insertMinSuffix(newOutFilePath)
|
|
90
|
+
|
|
91
|
+
if (!options.justMinified) console.log(`${pstyle.yellowBright + pstyle.bold}[script]${pstyle.reset} ${pstyle.dim}Compiled:${pstyle.reset} ${pstyle.italic + pstyle.underline}${newOutFilePath}${pstyle.reset} ${pstyle.greenBright}${fileSize(newOutFilePath)}${pstyle.reset} ${pstyle.green}(${buildTime(esbuildStart, esbuildEnd)})${pstyle.reset}`)
|
|
92
|
+
if (options.sourcemap) console.log(`${pstyle.yellowBright + pstyle.bold}[script]${pstyle.reset} ${pstyle.dim}Compiled:${pstyle.reset} ${pstyle.italic + pstyle.underline}${newOutFilePath}.map${pstyle.reset}`)
|
|
93
|
+
|
|
94
|
+
if (options.minify) {
|
|
95
|
+
const terserStart = performance.now()
|
|
96
|
+
Terser.minify(fs.readFileSync(newOutFilePath, 'utf-8'), { mangle: terserOpts.mangle }).then((result) => {
|
|
97
|
+
if (result.error) {
|
|
98
|
+
console.log(`${pstyle.redBright + pstyle.bold}[error]${pstyle.reset} Error occurred during JS minification: ${pstyle.dim}${result.error}${pstyle.reset}`)
|
|
99
|
+
} else {
|
|
100
|
+
if (this.banner) result.code = this.banner + '\n' + result.code
|
|
101
|
+
fs.writeFileSync(minPath, result.code)
|
|
102
|
+
const terserEnd = performance.now()
|
|
103
|
+
console.log(`${pstyle.yellowBright + pstyle.bold}[script]${pstyle.reset} ${pstyle.dim}Compiled:${pstyle.reset} ${pstyle.italic + pstyle.underline}${minPath}${pstyle.reset} ${pstyle.greenBright}${fileSize(minPath)}${pstyle.reset} ${pstyle.green}(${buildTime(terserStart, terserEnd)})${pstyle.reset}`)
|
|
104
|
+
}
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
if (options.justMinified) {
|
|
108
|
+
fs.unlinkSync(newOutFilePath)
|
|
109
|
+
}
|
|
110
|
+
} else {
|
|
111
|
+
fs.unlinkSync(minPath)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
}
|
|
116
|
+
}
|
package/lib/styles.js
ADDED
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
const autoprefixer = require('autoprefixer')
|
|
2
|
+
const cssnano = require('cssnano')
|
|
3
|
+
const fs = require('node:fs')
|
|
4
|
+
const helpers = require('./utils/helpers.js')
|
|
5
|
+
const path = require('node:path')
|
|
6
|
+
const { pathToFileURL } = require('node:url')
|
|
7
|
+
const postcss = require('postcss')
|
|
8
|
+
const sass = require('sass')
|
|
9
|
+
const PrintStyle = require('./utils/print-style.js')
|
|
10
|
+
|
|
11
|
+
const {
|
|
12
|
+
pathExists,
|
|
13
|
+
pathIsDirectory,
|
|
14
|
+
mkPath,
|
|
15
|
+
insertMinSuffix,
|
|
16
|
+
buildStyleOutputFilePath,
|
|
17
|
+
fillBannerTemplate,
|
|
18
|
+
buildTime,
|
|
19
|
+
fileSize
|
|
20
|
+
} = helpers
|
|
21
|
+
|
|
22
|
+
const pstyle = new PrintStyle()
|
|
23
|
+
|
|
24
|
+
function tryToFindFile(filePath, extensions) {
|
|
25
|
+
const fileExt = extensions.find(ext => fs.existsSync(`${filePath}.${ext}`))
|
|
26
|
+
if (fileExt) {
|
|
27
|
+
return `${filePath}.${fileExt}`
|
|
28
|
+
}
|
|
29
|
+
return null
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function sassPathResolver(url, resolvePath) {
|
|
33
|
+
if (fs.existsSync(url)) return new URL(url)
|
|
34
|
+
const resolvedPath = pathToFileURL(resolvePath)
|
|
35
|
+
if (!fs.existsSync(resolvedPath.pathname)) return null
|
|
36
|
+
const importPath = path.relative(process.cwd(), path.join(resolvedPath.pathname, url))
|
|
37
|
+
|
|
38
|
+
if (!fs.existsSync(importPath)) {
|
|
39
|
+
const correctFile = tryToFindFile(importPath, ['sass', 'scss', 'css'])
|
|
40
|
+
if (correctFile) return new URL(correctFile, resolvedPath)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (pathIsDirectory(importPath) && !pathExists(importPath, 'index.sass') && !pathExists(importPath, 'index.scss')) {
|
|
44
|
+
const correctFile = tryToFindFile(importPath, ['sass', 'scss', 'css'])
|
|
45
|
+
if (correctFile) return new URL(correctFile, resolvedPath)
|
|
46
|
+
|
|
47
|
+
if (!pathExists(importPath, 'package.json')) return null
|
|
48
|
+
|
|
49
|
+
const pkg = require(path.join(importPath, 'package.json'))
|
|
50
|
+
|
|
51
|
+
if (pkg.sass) return new URL(path.join(importPath, pkg.sass), resolvedPath)
|
|
52
|
+
if (pkg.css) return new URL(path.join(importPath, pkg.css), resolvedPath)
|
|
53
|
+
|
|
54
|
+
const basename = path.basename(pkg.main)
|
|
55
|
+
if (pkg.main && /(\.sass|\.scss|\.css)$/i.test(basename)) return new URL(path.join(importPath, pkg.main), resolvedPath)
|
|
56
|
+
return null
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return new URL(importPath, resolvedPath)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
module.exports = class Styles {
|
|
63
|
+
constructor(config) {
|
|
64
|
+
this.config = config
|
|
65
|
+
this.banner = config.banner ? fillBannerTemplate(config.banner) : null
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
compile() {
|
|
69
|
+
if (!this.config.styles) return
|
|
70
|
+
this.config.styles = Array.isArray(this.config.styles) ? this.config.styles : [this.config.styles]
|
|
71
|
+
for (const styleEntry of this.config.styles) {
|
|
72
|
+
if (styleEntry.in && styleEntry.out && pathExists(styleEntry.in)) {
|
|
73
|
+
mkPath(styleEntry.out)
|
|
74
|
+
this.compileEntry(styleEntry.in, styleEntry.out, styleEntry.options)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
compileEntry(infilePath, outfilePath, options = {}) {
|
|
80
|
+
const includePaths = this.config.includePaths || []
|
|
81
|
+
|
|
82
|
+
const opts = {
|
|
83
|
+
sourceMap: false,
|
|
84
|
+
sourceMapIncludeSources: false,
|
|
85
|
+
importers: [{
|
|
86
|
+
// Resolve `includePaths`.
|
|
87
|
+
findFileUrl(url) {
|
|
88
|
+
for (const includePath of includePaths) {
|
|
89
|
+
const resolvedPath = sassPathResolver(url, includePath)
|
|
90
|
+
if (resolvedPath) return resolvedPath
|
|
91
|
+
}
|
|
92
|
+
return null
|
|
93
|
+
}
|
|
94
|
+
}]
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (options.sourcemap) {
|
|
98
|
+
opts.sourceMap = options.sourcemap
|
|
99
|
+
opts.sourceMapIncludeSources = options.sourcemap
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
outfilePath = buildStyleOutputFilePath(infilePath, outfilePath, options)
|
|
103
|
+
|
|
104
|
+
const cssStart = performance.now()
|
|
105
|
+
const compiledSass = sass.compile(infilePath, opts)
|
|
106
|
+
const mapsrc = options.sourcemap ? `\n/*# sourceMappingURL=${path.basename(outfilePath)}.map */` : ''
|
|
107
|
+
if (this.banner) compiledSass.css = this.banner + '\n' + compiledSass.css
|
|
108
|
+
fs.writeFileSync(outfilePath, compiledSass.css + mapsrc)
|
|
109
|
+
const cssEnd = performance.now()
|
|
110
|
+
if (!options.justMinified) console.log(`${pstyle.magentaBright + pstyle.bold}[style]${pstyle.reset} ${pstyle.dim}Compiled:${pstyle.reset} ${pstyle.italic + pstyle.underline}${outfilePath}${pstyle.reset} ${pstyle.greenBright}${fileSize(outfilePath)}${pstyle.reset} ${pstyle.green}(${buildTime(cssStart, cssEnd)})${pstyle.reset}`)
|
|
111
|
+
|
|
112
|
+
if (compiledSass.sourceMap) {
|
|
113
|
+
if (this.banner) compiledSass.sourceMap.mappings = ';' + compiledSass.sourceMap.mappings
|
|
114
|
+
fs.writeFileSync(`${outfilePath}.map`, JSON.stringify(compiledSass.sourceMap))
|
|
115
|
+
console.log(`${pstyle.magentaBright + pstyle.bold}[style]${pstyle.reset} ${pstyle.dim}Compiled:${pstyle.reset} ${pstyle.italic + pstyle.underline}${outfilePath}.map${pstyle.reset}`)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const cssMinStart = performance.now()
|
|
119
|
+
const minPath = insertMinSuffix(outfilePath)
|
|
120
|
+
if (options.minify) {
|
|
121
|
+
postcss([autoprefixer, cssnano]).process(compiledSass.css, {
|
|
122
|
+
from: outfilePath,
|
|
123
|
+
to: minPath
|
|
124
|
+
}).then(result => {
|
|
125
|
+
if (this.banner) result.css = this.banner + '\n' + result.css
|
|
126
|
+
fs.writeFileSync(minPath, result.css)
|
|
127
|
+
const cssMinEnd = performance.now()
|
|
128
|
+
console.log(`${pstyle.magentaBright + pstyle.bold}[style]${pstyle.reset} ${pstyle.dim}Compiled:${pstyle.reset} ${pstyle.italic + pstyle.underline}${minPath}${pstyle.reset} ${pstyle.greenBright}${fileSize(minPath)}${pstyle.reset} ${pstyle.green}(${buildTime(cssMinStart, cssMinEnd)})${pstyle.reset}`)
|
|
129
|
+
}).catch((error) => {
|
|
130
|
+
console.log(`${pstyle.redBright + pstyle.bold}[error]${pstyle.reset} Error occurred during CSS minification: ${pstyle.dim}${error}${pstyle.reset}`)
|
|
131
|
+
})
|
|
132
|
+
|
|
133
|
+
if (options.justMinified) {
|
|
134
|
+
fs.unlinkSync(outfilePath)
|
|
135
|
+
}
|
|
136
|
+
} else {
|
|
137
|
+
fs.unlinkSync(minPath)
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
const fs = require('node:fs')
|
|
2
|
+
const path = require('node:path')
|
|
3
|
+
|
|
4
|
+
function pathExists() {
|
|
5
|
+
return fs.existsSync(path.join(...arguments))
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function pathIsDirectory() {
|
|
9
|
+
return fs.lstatSync(path.join(...arguments)).isDirectory()
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function mkPath(filePath) {
|
|
13
|
+
const dirPath = path.dirname(filePath)
|
|
14
|
+
if (!fs.existsSync(dirPath)) fs.mkdirSync(dirPath, { recursive: true })
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function pathForFile(filePath) {
|
|
18
|
+
return /\./.test(path.basename(filePath))
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function insertMinSuffix(filePath) {
|
|
22
|
+
const { name, ext } = path.parse(filePath)
|
|
23
|
+
return path.join(path.dirname(filePath), `${name}.min${ext}`)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function buildStyleOutputFilePath(inputPath, outputPath) {
|
|
27
|
+
if (pathForFile(outputPath)) return outputPath
|
|
28
|
+
const { name } = path.parse(inputPath)
|
|
29
|
+
return path.join(path.join(outputPath, `${name}.css`))
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function buildScriptOutputFilePath(inputPath, outputPath) {
|
|
33
|
+
if (pathForFile(outputPath)) return outputPath
|
|
34
|
+
const { name, ext } = path.parse(inputPath)
|
|
35
|
+
return path.join(path.join(outputPath, `${name}${ext.replace('t', 'j')}`))
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function fillBannerTemplate(template, packagesPath) {
|
|
39
|
+
packagesPath = packagesPath || process.cwd()
|
|
40
|
+
const packagesFilePath = path.join(packagesPath, 'package.json')
|
|
41
|
+
if (!pathExists(packagesFilePath)) return template
|
|
42
|
+
const pkg = require(packagesFilePath)
|
|
43
|
+
const { name, version, homepage, description, license, author } = pkg
|
|
44
|
+
const year = new Date().getFullYear()
|
|
45
|
+
|
|
46
|
+
return template
|
|
47
|
+
.replace(/{{\s?name\s?}}/g, name)
|
|
48
|
+
.replace(/{{\s?version\s?}}/g, version)
|
|
49
|
+
.replace(/{{\s?homepage\s?}}/g, homepage)
|
|
50
|
+
.replace(/{{\s?description\s?}}/g, description)
|
|
51
|
+
.replace(/{{\s?author\s?}}/g, author)
|
|
52
|
+
.replace(/{{\s?license\s?}}/g, license)
|
|
53
|
+
.replace(/{{\s?year\s?}}/g, year)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function buildTime(start, end) {
|
|
57
|
+
const time = Math.round(end - start)
|
|
58
|
+
if (time < 1000) return `${time}ms`
|
|
59
|
+
if (time < 60 * 1000) return `${(time / 1000).toFixed(0)}s ${time % 1000}ms`
|
|
60
|
+
return `${(time / 1000 / 60).toFixed(0)}m ${((time / 1000) % 60).toFixed(0)}s ${time % 1000}ms`
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function fileSize(filePath) {
|
|
64
|
+
const stats = fs.statSync(filePath)
|
|
65
|
+
const fileSizeInBytes = stats.size
|
|
66
|
+
if (fileSizeInBytes < 1000) return `${fileSizeInBytes}B`
|
|
67
|
+
if (fileSizeInBytes < 1000 * 1000) return `${(fileSizeInBytes / 1000).toFixed(0)}KB`
|
|
68
|
+
if (fileSizeInBytes < 1000 * 1000 * 1000) return `${(fileSizeInBytes / 1000 / 1000).toFixed(0)}MB ${fileSizeInBytes % (1000 * 1000)}KB`
|
|
69
|
+
return fileSizeInBytes
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
exports.pathExists = pathExists
|
|
73
|
+
exports.pathIsDirectory = pathIsDirectory
|
|
74
|
+
exports.mkPath = mkPath
|
|
75
|
+
exports.pathForFile = pathForFile
|
|
76
|
+
exports.insertMinSuffix = insertMinSuffix
|
|
77
|
+
exports.buildStyleOutputFilePath = buildStyleOutputFilePath
|
|
78
|
+
exports.buildScriptOutputFilePath = buildScriptOutputFilePath
|
|
79
|
+
exports.fillBannerTemplate = fillBannerTemplate
|
|
80
|
+
exports.buildTime = buildTime
|
|
81
|
+
exports.fileSize = fileSize
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
module.exports = class PrintStyle {
|
|
2
|
+
reset = '\x1b[0m'
|
|
3
|
+
bold = '\x1b[1m'
|
|
4
|
+
dim = '\x1b[2m'
|
|
5
|
+
italic = '\x1b[3m'
|
|
6
|
+
underline = '\x1b[4m'
|
|
7
|
+
blink = '\x1b[5m'
|
|
8
|
+
inverse = '\x1b[7m'
|
|
9
|
+
hidden = '\x1b[8m'
|
|
10
|
+
strikethrough = '\x1b[9m'
|
|
11
|
+
black = '\x1b[30m'
|
|
12
|
+
red = '\x1b[31m'
|
|
13
|
+
redBright = '\x1b[91m'
|
|
14
|
+
green = '\x1b[32m'
|
|
15
|
+
greenBright = '\x1b[92m'
|
|
16
|
+
yellow = '\x1b[33m'
|
|
17
|
+
yellowBright = '\x1b[93m'
|
|
18
|
+
blue = '\x1b[34m'
|
|
19
|
+
blueBright = '\x1b[94m'
|
|
20
|
+
magenta = '\x1b[35m'
|
|
21
|
+
magentaBright = '\x1b[95m'
|
|
22
|
+
cyan = '\x1b[36m'
|
|
23
|
+
cyanBright = '\x1b[96m'
|
|
24
|
+
white = '\x1b[37m'
|
|
25
|
+
whiteBright = '\x1b[97m'
|
|
26
|
+
gray = '\x1b[90m'
|
|
27
|
+
bgBlack = '\x1b[40m'
|
|
28
|
+
bgRed = '\x1b[41m'
|
|
29
|
+
bgRedBright = '\x1b[101m'
|
|
30
|
+
bgGreen = '\x1b[42m'
|
|
31
|
+
bgGreenBright = '\x1b[102m'
|
|
32
|
+
bgYellow = '\x1b[43m'
|
|
33
|
+
bgYellowBright = '\x1b[103m'
|
|
34
|
+
bgBlue = '\x1b[44m'
|
|
35
|
+
bgBlueBright = '\x1b[104m'
|
|
36
|
+
bgMagenta = '\x1b[45m'
|
|
37
|
+
bgMagentaBright = '\x1b[105m'
|
|
38
|
+
bgCyan = '\x1b[46m'
|
|
39
|
+
bgCyanBright = '\x1b[106m'
|
|
40
|
+
bgWhite = '\x1b[47m'
|
|
41
|
+
bgWhiteBright = '\x1b[107m'
|
|
42
|
+
bgGray = '\x1b[100m'
|
|
43
|
+
bell = '\x07'
|
|
44
|
+
|
|
45
|
+
hexToRgb(hex) {
|
|
46
|
+
const sanitizedHex = hex.replace('#', '')
|
|
47
|
+
const red = parseInt(sanitizedHex.substring(0, 2), 16)
|
|
48
|
+
const green = parseInt(sanitizedHex.substring(2, 4), 16)
|
|
49
|
+
const blue = parseInt(sanitizedHex.substring(4, 6), 16)
|
|
50
|
+
|
|
51
|
+
return [red, green, blue]
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
terminalColorIndex(red, green, blue) {
|
|
55
|
+
return 16 +
|
|
56
|
+
Math.round(red / 255 * 5) * 36 +
|
|
57
|
+
Math.round(green / 255 * 5) * 6 +
|
|
58
|
+
Math.round(blue / 255 * 5)
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
color(hex) {
|
|
62
|
+
const [red, green, blue] = this.hexToRgb(hex)
|
|
63
|
+
|
|
64
|
+
return `\x1b[38;5;${this.terminalColorIndex(red, green, blue)}m`
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
background(hex) {
|
|
68
|
+
const [red, green, blue] = this.hexToRgb(hex)
|
|
69
|
+
|
|
70
|
+
return `\x1b[48;5;${this.terminalColorIndex(red, green, blue)}m`
|
|
71
|
+
}
|
|
72
|
+
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "poops",
|
|
3
3
|
"description": "A super simple bundler for simple web projects.",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.2",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"main": "poops.js",
|
|
7
7
|
"repository": "https://github.com/stamat/poops.git",
|
|
@@ -27,7 +27,9 @@
|
|
|
27
27
|
"cssnano": "^6.0.1",
|
|
28
28
|
"deepmerge": "^4.3.1",
|
|
29
29
|
"esbuild": "^0.18.4",
|
|
30
|
+
"glob": "^10.3.1",
|
|
30
31
|
"livereload": "^0.9.3",
|
|
32
|
+
"nunjucks": "^3.2.4",
|
|
31
33
|
"postcss": "^8.4.24",
|
|
32
34
|
"sass": "^1.63.4",
|
|
33
35
|
"serve-static": "^1.15.0",
|
|
@@ -36,7 +38,7 @@
|
|
|
36
38
|
"devDependencies": {
|
|
37
39
|
"eslint": "^8.42.0",
|
|
38
40
|
"eslint-config-standard": "^17.1.0",
|
|
39
|
-
"eslint-plugin-import": "^2.
|
|
41
|
+
"eslint-plugin-import": "^2.26.0",
|
|
40
42
|
"eslint-plugin-n": "^16.0.0",
|
|
41
43
|
"eslint-plugin-promise": "^6.1.1",
|
|
42
44
|
"sulphuris": "^1.0.5"
|