poops 1.0.19 → 1.1.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.
Files changed (51) hide show
  1. package/README.md +246 -104
  2. package/lib/copy.js +119 -0
  3. package/lib/markups.js +258 -162
  4. package/lib/scripts.js +9 -11
  5. package/lib/ssg.js +158 -0
  6. package/lib/styles.js +10 -76
  7. package/lib/utils/helpers.js +157 -47
  8. package/lib/utils/print-style.js +1 -1
  9. package/package.json +23 -10
  10. package/poops.js +102 -76
  11. package/.eslintrc.yml +0 -10
  12. package/.github/dependabot.yml +0 -9
  13. package/.nojekyll +0 -1
  14. package/changelog/blog-functionality.html +0 -58
  15. package/changelog/feed.rss +0 -31
  16. package/changelog/front-matter.html +0 -61
  17. package/changelog/index.html +0 -74
  18. package/changelog/markdown-support.html +0 -56
  19. package/example/dist/css/styles.css +0 -9475
  20. package/example/dist/css/styles.css.map +0 -1
  21. package/example/dist/css/styles.min.css +0 -2
  22. package/example/dist/js/scripts.js +0 -35
  23. package/example/dist/js/scripts.js.map +0 -7
  24. package/example/dist/js/scripts.min.js +0 -2
  25. package/example/src/js/main.ts +0 -28
  26. package/example/src/js/scripts/utils.ts +0 -16
  27. package/example/src/markup/_data/features.yaml +0 -9
  28. package/example/src/markup/_data/links.json +0 -10
  29. package/example/src/markup/_data/poops.yaml +0 -7
  30. package/example/src/markup/_layouts/blog.html +0 -35
  31. package/example/src/markup/_layouts/default.html +0 -93
  32. package/example/src/markup/_partials/heading.html +0 -10
  33. package/example/src/markup/_partials/site-header.html +0 -17
  34. package/example/src/markup/changelog/blog-functionality.md +0 -10
  35. package/example/src/markup/changelog/feed.rss +0 -23
  36. package/example/src/markup/changelog/front-matter.md +0 -12
  37. package/example/src/markup/changelog/index.html +0 -30
  38. package/example/src/markup/changelog/markdown-support.md +0 -7
  39. package/example/src/markup/index.html +0 -33
  40. package/example/src/scss/_config.scss +0 -22
  41. package/example/src/scss/index.scss +0 -4
  42. package/example/src/scss/style/index.scss +0 -13
  43. package/example/src/scss/style/test.css +0 -3
  44. package/index.html +0 -166
  45. package/poop.png +0 -0
  46. package/poops.json +0 -55
  47. package/script/build +0 -3
  48. package/script/publish +0 -138
  49. package/script/server +0 -3
  50. package/test.html +0 -96
  51. package/tsconfig.json +0 -16
package/README.md CHANGED
@@ -1,4 +1,5 @@
1
1
  # 💩 Poops [![npm version](https://img.shields.io/npm/v/poops)](https://www.npmjs.com/package/poops)
2
+
2
3
  Straightforward, no-bullshit bundler for the web.
3
4
 
4
5
  > When your day is long
@@ -17,7 +18,7 @@ Straightforward, no-bullshit bundler for the web.
17
18
 
18
19
  [R.E.M. - Everybody Poops :poop:](https://www.youtube.com/watch?v=5rOiW_xY-kc)
19
20
 
20
- ----
21
+ ---
21
22
 
22
23
  Intuitive with a minimal learning curve and minimal docs, utilizing the most efficient transpilers and compilers available (like [dart-sass](https://sass-lang.com/dart-sass) and [esbuild](https://esbuild.github.io/)) Poops aims to be the simplest bundler option there is. If it's not, please do contribute so we can make it so! 🙏 All ideas and contributions are welcome.
23
24
 
@@ -25,20 +26,20 @@ It uses a simple config file where you define your input and output paths and it
25
26
 
26
27
  ## Features
27
28
 
28
- * Bundles SCSS/SASS to CSS
29
- * Uses [dart-sass](https://sass-lang.com/dart-sass) for SCSS/SASS bundling
30
- * Bundles JS/TS to IIFE/ESM/CJS
31
- * Uses [esbuild](https://esbuild.github.io/) for bundling and trinspiling JS/TS to IIFE/ESM/CJS
32
- * Optional JS and CSS minification using [esbuild](https://esbuild.github.io/)
33
- * Can produce minified code simultaneously with non-minified code! (cause I always forget to minify my code for production)
34
- * Supports source maps only for non minified - non production code (optional)
35
- * Supports multiple input and output paths
36
- * Resolves node modules
37
- * Can add a templatable banner to output files (optional)
38
- * Static site generation with [nunjucks](https://mozilla.github.io/nunjucks/) templating, with blogging option (optional)
39
- * Has a configurable local server (optional)
40
- * Rebuilds on file changes (optional)
41
- * Live reloads on file changes (optional)
29
+ - Bundles SCSS/SASS to CSS
30
+ - Uses [dart-sass](https://sass-lang.com/dart-sass) for SCSS/SASS bundling
31
+ - Bundles JS/TS/JSX/TSX to IIFE/ESM/CJS
32
+ - Uses [esbuild](https://esbuild.github.io/) for bundling and transpiling JS/TS/JSX/TSX to IIFE/ESM/CJS
33
+ - Optional JS and CSS minification using [esbuild](https://esbuild.github.io/)
34
+ - Can produce minified code simultaneously with non-minified code! (cause I always forget to minify my code for production)
35
+ - Supports source maps only for non minified - non production code (optional)
36
+ - Supports multiple input and output paths
37
+ - Resolves node modules
38
+ - Can add a templatable banner to output files (optional)
39
+ - Static site generation with [nunjucks](https://mozilla.github.io/nunjucks/) templating, with blogging option (optional)
40
+ - Has a configurable local server (optional)
41
+ - Rebuilds on file changes (optional)
42
+ - Live reloads on file changes (optional)
42
43
 
43
44
  ## Quick Start
44
45
 
@@ -82,26 +83,30 @@ Just create a `poops.json` file in the root of your project and add the followin
82
83
 
83
84
  ```json
84
85
  {
85
- "scripts": [{
86
- "in": "example/src/js/main.ts",
87
- "out": "example/dist/js/scripts.js",
88
- "options": {
89
- "sourcemap": true,
90
- "minify": true,
91
- "justMinified": false,
92
- "format": "iife",
93
- "target": "es2019"
86
+ "scripts": [
87
+ {
88
+ "in": "example/src/js/main.ts",
89
+ "out": "example/dist/js/scripts.js",
90
+ "options": {
91
+ "sourcemap": true,
92
+ "minify": true,
93
+ "justMinified": false,
94
+ "format": "iife",
95
+ "target": "es2019"
96
+ }
94
97
  }
95
- }],
96
- "styles": [{
97
- "in": "example/src/scss/index.scss",
98
- "out": "example/dist/css/styles.css",
99
- "options": {
100
- "sourcemap": true,
101
- "minify": true,
102
- "justMinified": false
98
+ ],
99
+ "styles": [
100
+ {
101
+ "in": "example/src/scss/index.scss",
102
+ "out": "example/dist/css/styles.css",
103
+ "options": {
104
+ "sourcemap": true,
105
+ "minify": true,
106
+ "justMinified": false
107
+ }
103
108
  }
104
- }],
109
+ ],
105
110
  "markup": {
106
111
  "in": "example/src/markup",
107
112
  "out": "/",
@@ -120,18 +125,20 @@ Just create a `poops.json` file in the root of your project and add the followin
120
125
  ]
121
126
  }
122
127
  },
128
+ "copy": [
129
+ {
130
+ "in": "example/src/static",
131
+ "out": "example/dist"
132
+ }
133
+ ],
123
134
  "banner": "/* {{ name }} v{{ version }} | {{ homepage }} | {{ license }} License */",
124
- "serve" : {
135
+ "serve": {
125
136
  "port": 4040,
126
137
  "base": "/"
127
138
  },
128
139
  "livereload": true,
129
- "watch": [
130
- "src"
131
- ],
132
- "includePaths": [
133
- "node_modules"
134
- ]
140
+ "watch": ["src"],
141
+ "includePaths": ["node_modules"]
135
142
  }
136
143
  ```
137
144
 
@@ -141,19 +148,20 @@ You can freely remove the properties that you don't need. For example, if you do
141
148
 
142
149
  ### Scripts
143
150
 
144
- Scripts are bundled with [esbuild](https://esbuild.github.io/). You can specify multiple scripts to bundle. Each script has the following properties:
151
+ 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:
145
152
 
146
- * `in` - the input path, can be an array of file paths, but please just use one file path per script
147
- * `out` - the output path, can be a directory or a file path, but please just use it as a filename
148
- * `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.
153
+ - `in` - the input path, can be an array of file paths, but please just use one file path per script
154
+ - `out` - the output path, can be a directory or a file path, but please just use it as a filename
155
+ - `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.
149
156
 
150
157
  **Options:**
151
- * `sourcemap` - whether to generate sourcemaps or not, sourcemaps are generated only for non-minified files since they are useful for debugging. Default is `false`. This is a direct esbuild option
152
- * `minify` - whether to minify the output or not, minification is performed by `esbuild` and is only applied to non-minified files. Default is `false`
153
- * `justMinified` - whether you want to have a minified file as output only. Removes the non-minified file from the output. Useful for production builds. Default is `false`
154
- * `format` - the output format, can be `iife` or `esm` or `cjs` - this is a direct esbuild option
155
- * `target` - the target for the output, can be `es2018` or `es2019` or `es2020` or `esnext` for instance - this is a direct esbuild option
156
158
 
159
+ - `sourcemap` - whether to generate sourcemaps or not, sourcemaps are generated only for non-minified files since they are useful for debugging. Default is `false`. This is a direct esbuild option
160
+ - `minify` - whether to minify the output or not, minification is performed by `esbuild` and is only applied to non-minified files. Default is `false`
161
+ - `justMinified` - whether you want to have a minified file as output only. Removes the non-minified file from the output. Useful for production builds. Default is `false`
162
+ - `format` - the output format, can be `iife` or `esm` or `cjs` - this is a direct esbuild option
163
+ - `target` - the target for the output, can be `es2018` or `es2019` or `es2020` or `esnext` for instance - this is a direct esbuild option
164
+ - `jsx` - the JSX transform mode, can be `transform` (default) or `automatic`. Use `automatic` for React 17+ JSX runtime which doesn't require importing React in every file - this is a direct esbuild option
157
165
 
158
166
  `scripts` property can accept an array of script configurations or just a single script configuration. If you want to bundle multiple scripts, just add them to the `scripts` array:
159
167
 
@@ -186,20 +194,90 @@ Scripts are bundled with [esbuild](https://esbuild.github.io/). You can specify
186
194
  }
187
195
  ```
188
196
 
197
+ #### JSX/TSX (React) Example
198
+
199
+ To bundle a React app, just point `in` to your `.jsx` or `.tsx` entry file:
200
+
201
+ ```json
202
+ {
203
+ "scripts": [
204
+ {
205
+ "in": "src/js/app.jsx",
206
+ "out": "dist/js/app.js",
207
+ "options": {
208
+ "minify": true,
209
+ "format": "iife",
210
+ "jsx": "automatic"
211
+ }
212
+ }
213
+ ]
214
+ }
215
+ ```
216
+
217
+ Setting `jsx` to `automatic` uses React's JSX runtime (React 17+), so you don't need `import React from 'react'` in every file. If you omit `jsx` or set it to `transform`, the classic `React.createElement` transform is used.
218
+
189
219
  As noted earlier, if you don't want to bundle scripts, just remove the `scripts` property from the config.
190
220
 
221
+ ### React SSG (Static Site Generation)
222
+
223
+ SSG renders React components to HTML at build time, then hydrates them on the client. This means pages load with pre-rendered content instead of an empty `<div>`.
224
+
225
+ Each SSG entry has the following properties:
226
+
227
+ - `component` — the file that default-exports a React component (rendered server-side)
228
+ - `in` — the client entry file for hydration (bundled for browser)
229
+ - `out` — output path for the client bundle
230
+ - `inject` — Nunjucks variable name for the rendered HTML
231
+ - `options` — esbuild options (applied to the client bundle, same as `scripts` options)
232
+
233
+ ```json
234
+ {
235
+ "ssg": [
236
+ {
237
+ "component": "src/js/App.jsx",
238
+ "in": "src/js/app-hydrate.jsx",
239
+ "out": "dist/js/app-hydrate.js",
240
+ "inject": "app_html",
241
+ "options": {
242
+ "minify": true,
243
+ "target": "es2019"
244
+ }
245
+ }
246
+ ]
247
+ }
248
+ ```
249
+
250
+ In your Nunjucks templates, use the `inject` name to insert the rendered HTML:
251
+
252
+ ```html
253
+ <div id="root">{{ app_html | safe }}</div>
254
+ <script src="js/app-hydrate.min.js"></script>
255
+ ```
256
+
257
+ **How it works:**
258
+
259
+ 1. Poops bundles the component with `react-dom/server` for Node.js and calls `renderToString`
260
+ 2. The rendered HTML is stored and made available as a Nunjucks global variable
261
+ 3. The client entry is bundled for the browser (same as a regular `scripts` entry)
262
+ 4. At runtime, React hydrates the pre-rendered HTML, making it interactive
263
+
264
+ SSG runs after Styles but before Scripts and Markups in the build pipeline. Poops does not need `react` or `react-dom` as its own dependency — they are resolved from your project's `node_modules`.
265
+
266
+ In watch mode, changes to JSX/TSX files trigger SSG re-rendering followed by a markup recompile.
267
+
191
268
  ### Styles
192
269
 
193
270
  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:
194
271
 
195
- * `in` - the input path, accepts only a path to a file
196
- * `out` - the output path, can be a directory or a file path, but please just use it as a filename
197
- * `options` - the options for the bundler.
272
+ - `in` - the input path, accepts only a path to a file
273
+ - `out` - the output path, can be a directory or a file path, but please just use it as a filename
274
+ - `options` - the options for the bundler.
198
275
 
199
276
  **Options:**
200
- * `sourcemap` - whether to generate sourcemaps or not, sourcemaps are generated only for non-minified files since they are useful for debugging. Default is `false`
201
- * `minify` - whether to minify the output or not, minification is performed by `esbuild`. Default is `false`
202
- * `justMinified` - whether you want to have a minified file as output only. Removes the non-minified file from the output. Useful for production builds. Defaults to `false`.
277
+
278
+ - `sourcemap` - whether to generate sourcemaps or not, sourcemaps are generated only for non-minified files since they are useful for debugging. Default is `false`
279
+ - `minify` - whether to minify the output or not, minification is performed by `esbuild`. Default is `false`
280
+ - `justMinified` - whether you want to have a minified file as output only. Removes the non-minified file from the output. Useful for production builds. Defaults to `false`.
203
281
 
204
282
  `styles` property can accept an array of style configurations or just a single style configuration. If you want to bundle multiple styles, just add them to the `styles` array:
205
283
 
@@ -232,16 +310,15 @@ As noted earlier, if you don't want to bundle styles, just remove the `styles` p
232
310
 
233
311
  ### Markups 🚧
234
312
 
235
- Poops can generate static pages for you. This feature is still under development, but available for testing from the v1.0.2. Your markup is templated with [nunjucks](https://mozilla.github.io/nunjucks/). You can specify multiple markup directories to template. **It's currently recommended to specify only one markup directory since this feature is still WIP 🚧.** Each markup directory has the following properties:
313
+ Poops can generate static pages for you. This feature is still under development, but available for testing from the v1.0.2. Your markup is templated with [nunjucks](https://mozilla.github.io/nunjucks/). You can specify multiple markup directories to template. **It's currently recommended to specify only one markup directory since this feature is still WIP 🚧.** Each markup directory has the following properties:
236
314
 
237
- * `in` - the input path, can be a directory or a file path, but please just use it as a directory path for now. All files in this directory will be processed and the structure of the directory will be preserved in the output directory with exception to directories that begin with an underscore `_` will be ignored.
238
- * `out` - the output path, can be only a directory path (for now)
239
- * `site` (optional) - global data that will be available to all templates in the markup directory. Like site title, description, social media links, etc. You can then use this data in your templates `{{ site.title }}` for instance.
240
- * `data` (optional) - is an array of JSON or YAML data files, that once loaded will be available to all templates in the markup directory. If you provide a path to a file for instance `links.json` with a `facebook` property, you can then use this data in your templates `{{ links.facebook }}`. The base name of the file will be used as the variable name, with spaces, dashes and dots replaced with underscores. So `the awesome-links.json` will be available as `{{ the_awesome_links.facebook }}` in your templates. The root directory of the data files is `in` directory. So if you have a `data` directory in your `in` directory, you can specify the data files like this `data: ["data/links.json"]`. The same goes for the YAML files.
241
- * `includePaths` (WIP 🚧) - an array of paths to directories that will be added to the nunjucks include paths. Useful if you want to separate template partials and layouts. For instance, if you have a `_includes` directory with a `header.njk` partial that you want to include in your markup, you can add it to the include paths and then include the templates like this `{% include "header.njk" %}`, without specifying the full path to the partial. This will change in the future, to provide better ignore and include patterns for the markup directories.
242
-
243
- **💡 NOTE:** If, for instance, you are building a simple static onepager for your library, and want to pass a version variable from your `package.json`, Poops automatically reads your `package.json` if it exists in your working directory and sets the golobal variable `package` to the parsed JSON. So you can use it in your markup files, for example like this: `{{ package.version }}`.
315
+ - `in` - the input path, can be a directory or a file path, but please just use it as a directory path for now. All files in this directory will be processed and the structure of the directory will be preserved in the output directory with exception to directories that begin with an underscore `_` will be ignored.
316
+ - `out` - the output path, can be only a directory path (for now)
317
+ - `site` (optional) - global data that will be available to all templates in the markup directory. Like site title, description, social media links, etc. You can then use this data in your templates `{{ site.title }}` for instance.
318
+ - `data` (optional) - is an array of JSON or YAML data files, that once loaded will be available to all templates in the markup directory. If you provide a path to a file for instance `links.json` with a `facebook` property, you can then use this data in your templates `{{ links.facebook }}`. The base name of the file will be used as the variable name, with spaces, dashes and dots replaced with underscores. So `the awesome-links.json` will be available as `{{ the_awesome_links.facebook }}` in your templates. The root directory of the data files is `in` directory. So if you have a `data` directory in your `in` directory, you can specify the data files like this `data: ["data/links.json"]`. The same goes for the YAML files.
319
+ - `includePaths` - an array of paths to directories that will be added to the nunjucks include paths. Useful if you want to separate template partials and layouts. For instance, if you have a `_includes` directory with a `header.njk` partial that you want to include in your markup, you can add it to the include paths and then include the templates like this `{% include "header.njk" %}`, without specifying the full path to the partial. This will change in the future, to provide better ignore and include patterns for the markup directories.
244
320
 
321
+ **💡 NOTE:** If, for instance, you are building a simple static onepager for your library, and want to pass a version variable from your `package.json`, Poops automatically reads your `package.json` if it exists in your working directory and sets the global variable `package` to the parsed JSON. So you can use it in your markup files, for example like this: `{{ package.version }}`.
245
322
 
246
323
  Here is a sample markup configuration:
247
324
 
@@ -271,20 +348,84 @@ If your project doesn't have markups, you can remove the `markups` property from
271
348
 
272
349
  #### Custom Filters
273
350
 
274
- * `slugify` - slugifies a string. Usage: `{{ "My Awesome Title" | slugify }}` will output `my-awesome-title`
351
+ - `slugify` - slugifies a string. Usage: `{{ "My Awesome Title" | slugify }}` will output `my-awesome-title`
352
+
353
+ ### Copy
354
+
355
+ Configuration entry to copy files or directories - copy your static files like images and fonts, for instance, from `src` to `dist` directory. This feature was added to enable moving static files if you deploy GitHub pages via a GitHub action. If you don't want to use this feature, simply exclude the `copy` property from your config file.
356
+
357
+ Here is a sample copy configuration which will copy the `static` directory and it's contents to the `dist` directory:
358
+
359
+ ```JSON
360
+ {
361
+ "copy": {
362
+ "in": "src/static",
363
+ "out": "dist"
364
+ }
365
+ }
366
+ ```
367
+
368
+ You can specify a list of input paths and pass them to an output directory, for instance:
369
+
370
+ ```JSON
371
+ {
372
+ "copy": {
373
+ "in": ["src/static/ogimage.jpg", "src/static/favicon.ico", "src/fonts"],
374
+ "out": "dist"
375
+ }
376
+ }
377
+ ```
378
+
379
+ **💡 NOTE:** Copy property can also accept the list of objects containing `in` and `out` properties. For instance:
380
+
381
+ ```JSON
382
+ {
383
+ "copy": [
384
+ {
385
+ "in": ["src/static/ogimage.jpg", "src/static/favicon.ico", "src/fonts"],
386
+ "out": "dist"
387
+ },
388
+ {
389
+ "in": "images",
390
+ "out": "dist/static"
391
+ }
392
+ ]
393
+ }
394
+ ```
395
+
396
+ **💡 NOTE:** Copy can also accept **GLOB** and **EXTGLOB** patterns as input paths, except POSIX character classes (e.g. `[[:alpha:]]`):
397
+
398
+ ```JSON
399
+ {
400
+ "copy": {
401
+ "in": [
402
+ "images/**/awesome.{jpeg,jpg,png}",
403
+ "notes/info[0-9].txt",
404
+ "notes/doc?.txt",
405
+ "notes/memo*.txt",
406
+ "notes/log[!123a].txt",
407
+ "assets/!(vendor)/*.js",
408
+ "fonts/@(woff|woff2)/*.+(woff|woff2)",
409
+ "docs/?(intro|overview).md"
410
+ ],
411
+ "out": "dist"
412
+ }
413
+ }
414
+ ```
275
415
 
276
416
  ### Banner (optional)
277
417
 
278
418
  Here you can specify a banner that will be added to the top of the output files. It is templatable via mustache. The following variables are available from your project's `package.json`:
279
419
 
280
- * `name`
281
- * `version`
282
- * `homepage`
283
- * `license`
284
- * `author`
285
- * `description`
420
+ - `name`
421
+ - `version`
422
+ - `homepage`
423
+ - `license`
424
+ - `author`
425
+ - `description`
286
426
 
287
427
  Here is a sample banner template.
428
+
288
429
  ```
289
430
  /* {{ name }} v{{ version }} | {{ homepage }} | {{ license }} License */
290
431
  ```
@@ -294,20 +435,24 @@ You can always pass just a string, you don't have to template it.
294
435
  If you don't want to add a banner, just remove the `banner` property from the config.
295
436
 
296
437
  ### Local Server (optional)
438
+
297
439
  Sets up a local server for your project.
298
440
 
299
441
  Server options:
300
- * `port` - the port on which the server will run
301
- * `base` - the base path of the server, where your HTML files are located
442
+
443
+ - `port` - the port on which the server will run
444
+ - `base` - the base path of the server, where your HTML files are located
302
445
 
303
446
  If you don't want to run a local server, just remove the `serve` property from the config.
304
447
 
305
448
  ### Live Reload (optional)
449
+
306
450
  Sets up a livereload server for your project.
307
451
 
308
452
  Live reload options:
309
- * `port` - the port on which the livereload server will run
310
- * `exclude` - an array of files and directories to exclude from livereload
453
+
454
+ - `port` - the port on which the livereload server will run
455
+ - `exclude` - an array of files and directories to exclude from livereload
311
456
 
312
457
  `livereload` can only be `true`, which means that it will run on the default port (`35729`) or you can specify a port:
313
458
 
@@ -324,10 +469,7 @@ You can also exclude files and directories from livereload:
324
469
  ```json
325
470
  {
326
471
  "livereload": {
327
- "exclude": [
328
- "some_directory/**/*",
329
- "some_other_directory/**/*"
330
- ]
472
+ "exclude": ["some_directory/**/*", "some_other_directory/**/*"]
331
473
  }
332
474
  }
333
475
  ```
@@ -335,10 +477,14 @@ You can also exclude files and directories from livereload:
335
477
  In order for Livereload to work, you need to add the following script snippet to your HTML files in your development environment:
336
478
 
337
479
  ```html
338
- <script>document.write('<script src="http://'
339
- + (location.host || 'localhost').split(':')[0]
340
- + ':35729/livereload.js?snipver=1"></'
341
- + 'script>')</script>
480
+ <script>
481
+ document.write(
482
+ '<script src="http://' +
483
+ (location.host || "localhost").split(":")[0] +
484
+ ':35729/livereload.js?snipver=1"></' +
485
+ "script>",
486
+ );
487
+ </script>
342
488
  ```
343
489
 
344
490
  Be mindful of the port, if you have specified a custom port, you need to change the port in the snippet as well.
@@ -348,56 +494,52 @@ You can also use a browser extension for livereload, for instance here is one fo
348
494
  If you don't want to run livereload, just remove the `livereload` property from the config, or set it to false.
349
495
 
350
496
  ### Watch (optional)
497
+
351
498
  Sets up a watcher for your project which will rebuild your files on change.
352
499
 
353
500
  `watch` property accepts an array of paths to watch for changes. If you want to watch for changes in the `src` directory, just add it to the `watch` array:
354
501
 
355
502
  ```json
356
503
  {
357
- "watch": [
358
- "src"
359
- ]
504
+ "watch": ["src"]
360
505
  }
361
506
  ```
362
507
 
363
508
  If you don't want to watch for file changes, just remove the `watch` property from the config.
364
509
 
365
510
  ### Include Paths (optional)
511
+
366
512
  This property is used to specify paths that you want to resolve your imports from. Like `node_modules`. You don't need to specify the `includePaths`, `node_modules` are included by default. But if you do specify `includePaths`, you need to include `node_modules` as well, since this change will override the default behavior.
367
513
 
368
514
  Same as `watch` property, `includePaths` accepts an array of paths to include. If you want to include `lib` directory for instance, just add it to the `includePaths` array:
369
515
 
370
516
  ```json
371
517
  {
372
- "includePaths": [
373
- "node_modules", "lib"
374
- ]
518
+ "includePaths": ["node_modules", "lib"]
375
519
  }
376
520
  ```
377
521
 
378
522
  ## Todo
379
523
 
380
- * [ ] Run esbuild for each input path individually if there are multiple input paths
381
- * [ ] Styles `in` should be able to support array of inputs like we have it on scripts
382
- * [ ] Build a cli config creation helper tool. If the user doesn't have a config file, we can ask them a few questions and create a config file for them. Create Yeoman generator for poops projects.
383
- * [ ] Support for LESS styles... I guess... And Stylus... I guess...
384
- * [x] Add nunjucks static templating
385
- * [ ] Refactor nunjucks implementation
386
- * [ ] Complete documentation for nunjucks
387
- * [x] Add markdown support
388
- * [x] Front Matter support
389
- * [ ] Future implementation: alternative templating engine liquidjs?
390
- * [x] Future implementation: posts and custom collections, so we can have a real static site generator
391
- * [x] Collection pagination system
392
- * [x] Post published toggle
393
- * [x] RSS and ATOM generation for collections
394
- * [ ] Refactor!!!!
524
+ - [ ] Run esbuild for each input path individually if there are multiple input paths
525
+ - [ ] Styles `in` should be able to support array of inputs like we have it on scripts
526
+ - [ ] Build a cli config creation helper tool. If the user doesn't have a config file, we can ask them a few questions and create a config file for them. Create Yeoman generator for poops projects.
527
+ - [x] Add nunjucks static templating
528
+ - [ ] Refactor nunjucks implementation
529
+ - [ ] Complete documentation for nunjucks
530
+ - [x] Add markdown support
531
+ - [x] Front Matter support
532
+ - [x] Future implementation: posts and custom collections, so we can have a real static site generator
533
+ - [x] Collection pagination system
534
+ - [x] Post published toggle
535
+ - [x] RSS and ATOM generation for collections
536
+ - [ ] Support for images and creating srcsets
395
537
 
396
538
  ## Why?
397
539
 
398
540
  Why doesn't anyone maintain GULP anymore? Why does Parcel hate config files? Why are Rollup and Webpack so complex to setup for simple tasks? Vite???? What's going on?
399
541
 
400
- I'm tired... Tired of bullshit... I just want to bundle my scss/sass and/or my js/ts to css and iife/esm js, by providing input and output paths for both/one. And to be able to have minimal easily maintainable dependencies. I don't need plugins, I'll add the features manually for the practice I use. That's it. The f**king end.
542
+ I'm tired... Tired of bullshit... I just want to bundle my scss/sass and/or my js/ts to css and iife/esm js, by providing input and output paths for both/one. And to be able to have minimal easily maintainable dependencies. I don't need plugins, I'll add the features manually for the practice I use. That's it. The f\*\*king end.
401
543
 
402
544
  To better illustrate it, here is a sample diff of Poops replacing Rollup:
403
545
 
package/lib/copy.js ADDED
@@ -0,0 +1,119 @@
1
+ import { globSync, hasMagic } from 'glob'
2
+ import {
3
+ pathExists,
4
+ pathIsDirectory,
5
+ mkDir,
6
+ copyDirectory,
7
+ buildTime
8
+ } from './utils/helpers.js'
9
+ import fs from 'node:fs'
10
+ import path from 'node:path'
11
+ import PrintStyle from './utils/print-style.js'
12
+
13
+ const pstyle = new PrintStyle()
14
+
15
+ export default class Copy {
16
+ constructor(config) {
17
+ this.config = config
18
+ }
19
+
20
+ async execute() {
21
+ if (!this.config.copy) return
22
+ const copyStartTime = performance.now()
23
+ this.config.copy = Array.isArray(this.config.copy) ? this.config.copy : [this.config.copy]
24
+ let copyLastPath = ''
25
+ let copyPathCount = 0
26
+
27
+ for (const copyEntry of this.config.copy) {
28
+ if (!copyEntry.in || !copyEntry.out) {
29
+ console.log(`${pstyle.redBright + pstyle.bold}[error]${pstyle.reset}[copy] ${pstyle.dim}Cannot copy. Missing 'in' or 'out' property in copy entry:${pstyle.reset} ${JSON.stringify(copyEntry)}`)
30
+ continue
31
+ }
32
+
33
+ const outPath = path.resolve(process.cwd(), copyEntry.out)
34
+ const inEntries = Array.isArray(copyEntry.in) ? copyEntry.in : [copyEntry.in]
35
+
36
+ for (const inEntry of inEntries) {
37
+ const hasGlobMagic = typeof hasMagic === 'function'
38
+ ? hasMagic(inEntry)
39
+ : ['*', '?', '[', ']', '{', '}', '(', ')', '!'].some((ch) => inEntry.includes(ch))
40
+ let matches = []
41
+ try {
42
+ if (typeof globSync === 'function') {
43
+ matches = globSync(inEntry, { dot: true, nodir: false })
44
+ }
45
+ } catch (err) {
46
+ matches = []
47
+ }
48
+
49
+ if (matches.length > 0) {
50
+ for (const matchedPath of matches) {
51
+ if (pathExists(matchedPath)) {
52
+ await this.copyEntry(matchedPath, outPath)
53
+ copyLastPath = inEntry
54
+ copyPathCount++
55
+ } else {
56
+ console.log(`${pstyle.redBright + pstyle.bold}[error]${pstyle.reset}[copy] ${pstyle.dim}Cannot copy. Source path does not exist:${pstyle.reset} ${matchedPath}`)
57
+ }
58
+ }
59
+ continue
60
+ }
61
+
62
+ if (pathExists(inEntry)) {
63
+ await this.copyEntry(inEntry, outPath)
64
+ copyLastPath = inEntry
65
+ copyPathCount++
66
+ } else {
67
+ if (hasGlobMagic) {
68
+ console.log(`${pstyle.redBright + pstyle.bold}[error]${pstyle.reset}[copy] ${pstyle.dim}No files matched glob pattern:${pstyle.reset} ${inEntry}`)
69
+ } else {
70
+ console.log(`${pstyle.redBright + pstyle.bold}[error]${pstyle.reset}[copy] ${pstyle.dim}Cannot copy. Source path does not exist:${pstyle.reset} ${inEntry}`)
71
+ }
72
+ }
73
+ }
74
+ }
75
+
76
+ const copyEndTime = performance.now()
77
+ if (copyPathCount === 0) return
78
+ if (copyPathCount === 1) {
79
+ console.log(`${pstyle.green + pstyle.bold}[copy]${pstyle.reset} ${pstyle.dim}Copied${pstyle.reset} ${pstyle.italic + pstyle.underline}${copyLastPath}${pstyle.reset} ${pstyle.green}(${buildTime(copyStartTime, copyEndTime)})${pstyle.reset}`)
80
+ return
81
+ }
82
+
83
+ console.log(`${pstyle.green + pstyle.bold}[copy]${pstyle.reset} ${pstyle.dim}Copied${pstyle.reset} ${copyPathCount} paths ${pstyle.green}(${buildTime(copyStartTime, copyEndTime)})${pstyle.reset}`)
84
+ }
85
+
86
+ async unlink(file, copyPaths) {
87
+ if (!file || !copyPaths) return
88
+ if (!copyPaths.out || !copyPaths.in) return
89
+ if (!pathExists(copyPaths.out) || !pathExists(copyPaths.in)) return
90
+
91
+ if (pathIsDirectory(copyPaths.in)) {
92
+ const inBaseName = path.basename(copyPaths.in)
93
+ copyPaths.out = path.join(copyPaths.out, inBaseName)
94
+ }
95
+
96
+ file = file.replace(copyPaths.in, copyPaths.out)
97
+
98
+ const outputFilePath = path.join(process.cwd(), file)
99
+
100
+ if (pathExists(outputFilePath)) {
101
+ if (pathIsDirectory(outputFilePath)) {
102
+ fs.rmSync(outputFilePath, { recursive: true })
103
+ return
104
+ }
105
+ fs.unlinkSync(outputFilePath)
106
+ }
107
+ }
108
+
109
+ async copyEntry(inFilePath, outFilePath) {
110
+ const baseName = path.basename(inFilePath)
111
+ let outPath = outFilePath
112
+ mkDir(outFilePath)
113
+
114
+ if (pathIsDirectory(outFilePath)) {
115
+ outPath = path.join(outFilePath, baseName)
116
+ }
117
+ copyDirectory(inFilePath, outPath)
118
+ }
119
+ }