@static-pages/core 6.0.0 → 7.0.0-alpha.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/CHANGELOG.md CHANGED
@@ -1,5 +1,13 @@
1
1
  # CHANGELOG
2
2
 
3
+ ## 7.0.0-alpha.0
4
+ - New `CreateReader` and `CreateWriter` utilities available as `from` and `to` prop values.
5
+ - Writers now recieve an AsyncIterable as the previous method made too much confusion.
6
+ - Type defintion improvements.
7
+ - Test suite switched from tap to mocha for now.
8
+ - Coverage reports switched from nyc to c8.
9
+
10
+
3
11
  ## 6.0.0
4
12
  - Adopt JS iterator protocol for the writer callbacks.
5
13
  - Removed the `.teardown()` call on the writer when iteration finished. The iterator protocol gives better tools to express these logic.
package/README.md CHANGED
@@ -1,70 +1,76 @@
1
1
  # Static Pages / Core
2
2
 
3
- [![Build Status](https://github.com/staticpagesjs/core/actions/workflows/coveralls.yaml/badge.svg)](https://github.com/staticpagesjs/core/actions/workflows/coveralls.yaml)
3
+ [![Build Status](https://github.com/staticpagesjs/core/actions/workflows/build.yaml/badge.svg)](https://github.com/staticpagesjs/core/actions/workflows/build.yaml)
4
4
  [![Coverage Status](https://coveralls.io/repos/github/staticpagesjs/core/badge.svg?branch=master)](https://coveralls.io/github/staticpagesjs/core?branch=master)
5
5
  ![npms.io (quality)](https://img.shields.io/npms-io/quality-score/@static-pages/core?label=quality)
6
- ![Maintenance](https://img.shields.io/maintenance/yes/2023)
7
-
8
- This package contains only the core; this means it does not provide CLI support or readers and writers.
9
- You can import this library to your JS project then add your own controllers, readers and writers.
6
+ ![Maintenance](https://img.shields.io/maintenance/yes/2024)
10
7
 
11
8
  Yet another static pages generator?
12
9
  Yes! Because I browsed the whole jamstack scene, but could not find one which
13
- 1. uses MVC pattern
14
- 2. can read input from any source (YAML, JSON, front-matter style markdowns, database etc.)
15
- 3. can render with any template engine (Twig, ejs, Pug, Mustache etc.)
16
- 4. supports incremental builds
17
- 5. has a flexible CLI tool (see [@static-pages/cli](https://www.npmjs.com/package/@static-pages/cli) on npm)
18
- 6. has a Docker image (see [staticpages/cli](https://hub.docker.com/repository/docker/staticpages/cli) on dockerhub)
19
- 7. written in JS (preferably TypeScript)
20
- 8. easy to extend with JS code
21
- 9. learning and using is easy (Gatsby, Hugo, Jekyll, Eleventy etc. are so cool but harder to learn and configure)
10
+ 1. can read input from any source (YAML, JSON, front-matter style markdowns, database etc.)
11
+ 2. can render with any template engine (Twig, ejs, Pug, Mustache etc.)
12
+ 3. written in JS (preferably TypeScript)
13
+ 4. easy to extend with JS code
14
+ 5. supports incremental builds
15
+ 6. uses MVC pattern
16
+ 7. learning and using is easy (Gatsby, Hugo, Jekyll, Eleventy etc. are so cool but harder to learn and configure)
22
17
 
23
18
  And because I wrote a ton of custom static generators before; I tought I can improve the concepts to a point where its (hopefully) useful for others.
24
19
 
25
20
  ## Where should I use this?
26
- This project targets small and medium sized projects. The rendering process tries to be as fast as possible so its also useful when you need performance.
21
+
22
+ This project targets small and medium sized websites. The rendering process tries to be as fast as possible so its also useful when you need performance.
27
23
 
28
24
  ## Documentation
29
- [Visit the project page.](https://staticpagesjs.github.io/)
25
+
26
+ For detailed information, visit the [project page](https://staticpagesjs.github.io/).
30
27
 
31
28
  ## Usage
32
- - __Readers__ provides an iterable list of page data.
33
- - __Controllers__ can manipulate and extend each data object.
34
- - __Writers__ render the final output for you.
35
29
 
36
30
  ```js
37
31
  import staticPages from '@static-pages/core';
38
- import markdownReader from '@static-pages/markdown-reader';
39
- import yamlReader from '@static-pages/yaml-reader';
40
- import twigWriter from '@static-pages/twig-writer';
41
-
42
- staticPages({
43
- from: markdownReader({
44
- pattern: "pages/**/*.md"
45
- }),
46
- to: twigWriter({
47
- view: "content.html.twig",
48
- viewsDir: "path/to/views/folder",
49
- outDir: "path/to/output/folder",
50
- }),
51
- controller(data) {
52
- data.timestamp = new Date().toJSON(); // adds a "timestamp" variable
53
- return data; // returning the data is required if you want to send it to the renderer
54
- }
55
- }, {
56
- from: yamlReader({ // assume we have the home page data in yaml format.
57
- pattern: "home/*.yaml" // <-- reads home/en.yaml, home/de.yaml, home/fr.yaml etc.
58
- }),
59
- to: twigWriter({
60
- view: "home.html.twig",
61
- viewsDir: "path/to/views/folder",
62
- outDir: "path/to/output/folder",
63
- }),
32
+ import twig from '@static-pages/twig';
33
+
34
+ // Default options for every `Route` via .with() call.
35
+ const generate = staticPages.with({
36
+ to: {
37
+ render: twig({
38
+ viewsDir: 'path/to/views/folder'
39
+ }),
40
+ },
64
41
  controller(data) {
65
- data.commitHash = yourGetCommitHashFn();
42
+ // adds a 'now' variable to the template context
43
+ data.now = new Date().toJSON();
44
+
45
+ // returning the data is required
66
46
  return data;
67
47
  }
48
+ });
49
+
50
+ // Generate every document type as a page.
51
+ // One route equals one batch of similar pages.
52
+ generate({
53
+ from: {
54
+ cwd: 'pages',
55
+ pattern: '**/*.md',
56
+ },
57
+ }, {
58
+ // Any Iterable or AsyncIterable also accepted eg. an array
59
+ from: [
60
+ { title: 'About', url: 'about', body: 'About us content' },
61
+ { title: 'Privacy', url: 'privacy', body: 'Privacy content' },
62
+ ],
63
+ }, {
64
+ from: {
65
+ cwd: 'home',
66
+ pattern: '*.yaml',
67
+ },
68
+ to: {
69
+ render: twig({
70
+ view: 'home.html.twig',
71
+ viewsDir: 'path/to/views/folder',
72
+ }),
73
+ },
68
74
  })
69
75
  .catch(error => {
70
76
  console.error('Error:', error);
@@ -72,24 +78,150 @@ staticPages({
72
78
  });
73
79
  ```
74
80
 
75
- ## `staticPages(...routes: Route[])`
81
+ ### Notes
76
82
 
77
- Each route consists of a `from`, `to` and optionally a `controller` property matching the definition below.
83
+ > The `controller` may return with multiple documents, each will be rendered as a separate page. Alternatively it may return `undefined` to prevent the rendering of the current document.
84
+
85
+ > The `from` parameter can also recieve an `Iterable` or an `AsyncIterable` type!
86
+
87
+ > The `to` parameter can also recieve a `function` that handles the document rendering and storing!
88
+
89
+
90
+ ## `staticPages(...routes: Route[]): Promise<void>`
91
+
92
+ Each route consists of a `from`, `to` and a `controller` property matching the definition below.
78
93
 
79
94
  ```ts
80
- type Data = Record<string, unknown>;
81
- type Route = {
82
- from: Iterable<Data> | AsyncIterable<Data>;
83
- to(data: IteratorResult<Data>): void | Promise<void>;
84
- controller?(data: Data): void | Data | Data[] | Promise<void | Data | Data[]>;
85
- };
95
+ interface Route<F, T> {
96
+ from?: Iterable<F> | AsyncIterable<F> | CreateReaderOptions<F>;
97
+ to?: { (data: AsyncIterable<T>): MaybePromise<void>; } | CreateWriterOptions<T>;
98
+ controller?(data: F): MaybePromise<undefined | T | Iterable<T> | AsyncIterable<T>>;
99
+ }
100
+
101
+ type MaybePromise<T> = T | Promise<T>;
102
+
103
+ interface CreateReaderOptions<T> {
104
+ // Handles file operations, defaults to nodejs `fs` module
105
+ fs?: Filesystem;
106
+ // Current working directory
107
+ cwd?: string;
108
+ // File patterns to include
109
+ pattern?: string | string[];
110
+ // File patterns to exclude
111
+ ignore?: string | string[];
112
+ // Callback to parse a file content into an object
113
+ parse?(content: Uint8Array | string, filename: string): MaybePromise<T>;
114
+ // Called on error
115
+ onError?(error: unknown): MaybePromise<void>;
116
+ }
117
+
118
+ interface CreateWriterOptions<T> {
119
+ // Handles file operations, defaults to nodejs `fs` module
120
+ fs?: Filesystem;
121
+ // Current working directory
122
+ cwd?: string;
123
+ // Callback that renders the document into a page
124
+ render?(data: T): MaybePromise<Uint8Array | string>;
125
+ // Callback that retrieves the filename (URL) of a page
126
+ name?(data: T): MaybePromise<string>;
127
+ // Called on error
128
+ onError?(error: unknown): MaybePromise<void>;
129
+ }
130
+
131
+ interface Stats {
132
+ isFile(): boolean;
133
+ isDirectory(): boolean;
134
+ }
135
+
136
+ interface Dirent {
137
+ name: string;
138
+ path: string;
139
+ isFile(): boolean;
140
+ isDirectory(): boolean;
141
+ }
142
+
143
+ interface Filesystem {
144
+ readdir(
145
+ path: string | URL,
146
+ options: {
147
+ encoding: 'utf8';
148
+ withFileTypes: false;
149
+ recursive: boolean;
150
+ },
151
+ callback: (err: Error | null, files: string[]) => void,
152
+ ): void;
153
+
154
+ readdir(
155
+ path: string | URL,
156
+ options: {
157
+ encoding: 'utf8';
158
+ withFileTypes: true;
159
+ recursive: boolean;
160
+ },
161
+ callback: (err: Error | null, files: Dirent[]) => void,
162
+ ): void;
163
+
164
+ readFile(
165
+ path: string | URL,
166
+ options: {
167
+ encoding: 'utf8';
168
+ },
169
+ callback: (err: Error | null, data: string) => void
170
+ ): void;
171
+
172
+ readFile(
173
+ path: string | URL,
174
+ options: null,
175
+ callback: (err: Error | null, data: Uint8Array) => void
176
+ ): void;
177
+
178
+ stat(
179
+ path: string | URL,
180
+ callback: (err: Error | null, stats: Stats) => void
181
+ ): void;
182
+
183
+ mkdir(
184
+ path: string | URL,
185
+ options: {
186
+ recursive: true;
187
+ },
188
+ callback: (err: Error | null, path?: string) => void
189
+ ): void;
190
+
191
+ writeFile(
192
+ path: string | URL,
193
+ data: string | Uint8Array,
194
+ callback: (err: Error | null) => void
195
+ ): void;
196
+ }
86
197
  ```
87
198
 
88
- ### Implementation Notes
199
+ ### `Filesystem` interface
200
+
201
+ When you use the `createReader` and `createWriter` interfaces to read and write documents, you can provide a `Filesystem` implementation. This interface is a minimal subset of the [NodeJS FS API](https://nodejs.org/api/fs.html). By default we use the built-in `node:fs` module.
202
+
203
+ ### `CreateReaderOptions` default parameters
204
+ - `fs`: the nodejs `fs` module
205
+ - `cwd`: `'pages'`
206
+ - `parse`: automatically parse `json`, `yaml`, `yml`, `md` or `markdown` extensions with `yaml` and `gray-matter` packages.
207
+ - `onError`: `(err) => { throw err; }`
208
+
209
+ ### `CreateWriterOptions` default parameters
210
+ - `fs`: the nodejs `fs` module
211
+ - `cwd`: `'public'`
212
+ - `name`: `(data) => data.url`
213
+ - `render`: `(data) => data.content`
214
+ - `onError`: `(err) => { throw err; }`
215
+
216
+
217
+ ## `staticPages.with(defaults: Partial<Route>): { (...routes: Partial<Route>[]): Promise<void>; }`
218
+
219
+ Preconfigures a separate instance of the `staticPages` call with the given default parameters.
220
+ These only works as fallback values, you can override every value later.
89
221
 
90
- - The `controller` may return an array of `Data` objects; each will be rendered as a separate page. Alternatively it may return `void` to prevent the rendering of the current data object.
222
+ If a `from` or `to` parameter is a plain object in both defaults and later at the route definition they will be merged (see usage example).
91
223
 
92
- - The writer function provided in the `to` property is expected to conform to the JavaScript iterator protocol in order to work. Specifically, this means that the function will be invoked with a `{ value: Data }` parameter for each data object, and will receive a `{ done: true }` parameter when it has finished processing the data and there is no more pages to write.
93
224
 
94
225
  ## Missing a feature?
95
- Create an issue describing your needs. If it fits the scope of the project I will implement it or you can implement it your own and submit a pull request.
226
+ Create an issue describing your needs!
227
+ If it fits the scope of the project I will implement it.
@@ -0,0 +1 @@
1
+ export declare function autoparse(content: string | Uint8Array, filename: string): any;
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.autoparse = void 0;
7
+ const yaml_1 = require("yaml");
8
+ const gray_matter_1 = __importDefault(require("gray-matter"));
9
+ function autoparse(content, filename) {
10
+ const extension = filename.split('.').pop().toLowerCase();
11
+ switch (extension) {
12
+ case 'json':
13
+ return JSON.parse(content.toString());
14
+ case 'yaml':
15
+ case 'yml':
16
+ return (0, yaml_1.parse)(content.toString());
17
+ case 'md':
18
+ case 'markdown':
19
+ const { data, content: markdownContent } = (0, gray_matter_1.default)(content.toString());
20
+ return { ...data, content: markdownContent };
21
+ }
22
+ throw new Error(`Could not parse document with '${extension}' extension.`);
23
+ }
24
+ exports.autoparse = autoparse;
@@ -0,0 +1,15 @@
1
+ import type { MaybePromise, Filesystem } from './helpers.js';
2
+ export declare namespace createReader {
3
+ interface Options<T> {
4
+ fs?: Filesystem;
5
+ cwd?: string;
6
+ pattern?: string | string[];
7
+ ignore?: string | string[];
8
+ parse?(content: Uint8Array | string, filename: string): MaybePromise<T>;
9
+ onError?(error: unknown): MaybePromise<void>;
10
+ }
11
+ }
12
+ export declare function createReader<T>({ fs, cwd, pattern, ignore, parse, onError, }?: createReader.Options<T>): AsyncGenerator<Awaited<T>, void, unknown>;
13
+ export declare namespace createReader {
14
+ var isOptions: <T>(x: unknown) => x is Options<T>;
15
+ }
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.createReader = void 0;
7
+ const picomatch_1 = __importDefault(require("picomatch"));
8
+ const autoparse_js_1 = require("./autoparse.js");
9
+ const helpers_js_1 = require("./helpers.js");
10
+ const node_path_1 = require("node:path");
11
+ const node_fs_1 = __importDefault(require("node:fs"));
12
+ async function* createReader({ fs = node_fs_1.default, cwd = 'pages', pattern, ignore, parse = autoparse_js_1.autoparse, onError = (error) => { throw error; }, } = {}) {
13
+ if (!(0, helpers_js_1.isFilesystem)(fs))
14
+ throw new TypeError(`Expected Node FS compatible implementation at 'fs' property.`);
15
+ if (typeof cwd !== 'string')
16
+ throw new TypeError(`Expected 'string', recieved '${(0, helpers_js_1.getType)(cwd)}' at 'cwd' property.`);
17
+ if (!cwd)
18
+ throw new TypeError(`Expected non-empty string at 'cwd'.`);
19
+ if (typeof pattern !== 'undefined' && typeof pattern !== 'string' && !Array.isArray(pattern))
20
+ throw new TypeError(`Expected 'string' or 'string[]', recieved '${(0, helpers_js_1.getType)(pattern)}' at 'pattern' property.`);
21
+ if (typeof ignore !== 'undefined' && typeof ignore !== 'string' && !Array.isArray(ignore))
22
+ throw new TypeError(`Expected 'string' or 'string[]', recieved '${(0, helpers_js_1.getType)(ignore)}' at 'ignore' property.`);
23
+ if (typeof parse !== 'function')
24
+ throw new TypeError(`Expected 'function', recieved '${(0, helpers_js_1.getType)(parse)}' at 'parse' property.`);
25
+ if (typeof onError !== 'function')
26
+ throw new TypeError(`Expected 'function', recieved '${(0, helpers_js_1.getType)(onError)}' at 'onError' property.`);
27
+ let filenames = await new Promise((resolve, reject) => {
28
+ fs.readdir(cwd, { encoding: 'utf8', recursive: true, withFileTypes: true }, (err, files) => {
29
+ if (err)
30
+ reject(err);
31
+ else
32
+ resolve(files
33
+ .filter(x => x.isFile())
34
+ .map(x => (0, node_path_1.join)((0, node_path_1.relative)(cwd, x.path), x.name)));
35
+ });
36
+ });
37
+ if (typeof pattern !== 'undefined' || typeof ignore !== 'undefined') {
38
+ const filteredFilenames = [];
39
+ const isMatch = (0, picomatch_1.default)(pattern !== null && pattern !== void 0 ? pattern : '**/*', { ignore });
40
+ for (const filename of filenames) {
41
+ if (isMatch(filename)) {
42
+ filteredFilenames.push(filename);
43
+ }
44
+ }
45
+ filenames = filteredFilenames;
46
+ }
47
+ for (const filename of filenames) {
48
+ try {
49
+ const content = await new Promise((resolve, reject) => {
50
+ fs.readFile((0, node_path_1.join)(cwd, filename), null, (err, data) => {
51
+ if (err)
52
+ reject(err);
53
+ else
54
+ resolve(data);
55
+ });
56
+ });
57
+ yield await parse(content, filename);
58
+ }
59
+ catch (error) {
60
+ await onError(error);
61
+ }
62
+ }
63
+ }
64
+ exports.createReader = createReader;
65
+ createReader.isOptions = (x) => {
66
+ return x == undefined || (!!x && typeof x === 'object' && !(0, helpers_js_1.isIterable)(x) && !(0, helpers_js_1.isAsyncIterable)(x));
67
+ };
@@ -0,0 +1,14 @@
1
+ import type { MaybePromise, Filesystem } from './helpers.js';
2
+ export declare namespace createWriter {
3
+ interface Options<T> {
4
+ fs?: Filesystem;
5
+ cwd?: string;
6
+ name?(data: T): MaybePromise<string>;
7
+ render?(data: T): MaybePromise<Uint8Array | string>;
8
+ onError?(error: unknown): MaybePromise<void>;
9
+ }
10
+ }
11
+ export declare function createWriter<T>({ fs, cwd, name, render, onError, }?: createWriter.Options<T>): (iterable: Iterable<T> | AsyncIterable<T>) => Promise<void>;
12
+ export declare namespace createWriter {
13
+ var isOptions: <T>(x: unknown) => x is Options<T>;
14
+ }
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.createWriter = void 0;
27
+ const helpers_js_1 = require("./helpers.js");
28
+ const node_path_1 = require("node:path");
29
+ const nodeFs = __importStar(require("node:fs"));
30
+ const defaultNamer = (data) => {
31
+ if (!!data && typeof data === 'object' && 'url' in data && typeof data.url === 'string') {
32
+ return data.url.concat('.html');
33
+ }
34
+ throw new Error(`Missing 'url' field in the document.`);
35
+ };
36
+ const defaultRenderer = (data) => {
37
+ if (!!data && typeof data === 'object' && 'content' in data) {
38
+ return '' + data.content;
39
+ }
40
+ throw new Error(`Missing 'content' field in the document.`);
41
+ };
42
+ function createWriter({ fs = nodeFs, cwd = 'public', name = defaultNamer, render = defaultRenderer, onError = (error) => { throw error; }, } = {}) {
43
+ if (!(0, helpers_js_1.isFilesystem)(fs))
44
+ throw new TypeError(`Expected Node FS compatible implementation at 'fs' property.`);
45
+ if (typeof cwd !== 'string')
46
+ throw new TypeError(`Expected 'string', recieved '${(0, helpers_js_1.getType)(cwd)}' at 'cwd' property.`);
47
+ if (!cwd)
48
+ throw new TypeError(`Expected non-empty string at 'cwd'.`);
49
+ if (typeof render !== 'function')
50
+ throw new TypeError(`Expected 'function', recieved '${(0, helpers_js_1.getType)(render)}' at 'render' property.`);
51
+ if (typeof name !== 'function')
52
+ throw new TypeError(`Expected 'function', recieved '${(0, helpers_js_1.getType)(name)}' at 'name' property.`);
53
+ if (typeof onError !== 'function')
54
+ throw new TypeError(`Expected 'function', recieved '${(0, helpers_js_1.getType)(onError)}' at 'onError' property.`);
55
+ return async function (iterable) {
56
+ if (!(0, helpers_js_1.isIterable)(iterable) && !(0, helpers_js_1.isAsyncIterable)(iterable))
57
+ throw new TypeError(`Expected 'Iterable' or 'AsyncIterable' at callback.`);
58
+ for await (const data of iterable) {
59
+ try {
60
+ const filepath = cwd + '/' + await name(data);
61
+ const dirpath = (0, node_path_1.dirname)(filepath);
62
+ await new Promise((resolve, reject) => {
63
+ fs.stat(dirpath, (err, stats) => {
64
+ if (err) {
65
+ fs.mkdir(dirpath, { recursive: true }, (err) => {
66
+ if (err) {
67
+ reject(err);
68
+ }
69
+ else {
70
+ resolve(undefined);
71
+ }
72
+ });
73
+ }
74
+ else {
75
+ resolve(undefined);
76
+ }
77
+ });
78
+ });
79
+ const content = await render(data);
80
+ await new Promise((resolve, reject) => {
81
+ fs.writeFile(filepath, content, (err) => {
82
+ if (err)
83
+ reject(err);
84
+ else
85
+ resolve(undefined);
86
+ });
87
+ });
88
+ }
89
+ catch (error) {
90
+ await onError(error);
91
+ }
92
+ }
93
+ };
94
+ }
95
+ exports.createWriter = createWriter;
96
+ createWriter.isOptions = (x) => {
97
+ return x == undefined || (!!x && typeof x === 'object');
98
+ };
@@ -0,0 +1,37 @@
1
+ export type MaybePromise<T> = T | Promise<T>;
2
+ interface Stats {
3
+ isFile(): boolean;
4
+ isDirectory(): boolean;
5
+ }
6
+ interface Dirent {
7
+ name: string;
8
+ path: string;
9
+ isFile(): boolean;
10
+ isDirectory(): boolean;
11
+ }
12
+ export interface Filesystem {
13
+ readdir(path: string | URL, options: {
14
+ encoding: 'utf8';
15
+ withFileTypes: false;
16
+ recursive: boolean;
17
+ }, callback: (err: Error | null, files: string[]) => void): void;
18
+ readdir(path: string | URL, options: {
19
+ encoding: 'utf8';
20
+ withFileTypes: true;
21
+ recursive: boolean;
22
+ }, callback: (err: Error | null, files: Dirent[]) => void): void;
23
+ readFile(path: string | URL, options: {
24
+ encoding: 'utf8';
25
+ }, callback: (err: Error | null, data: string) => void): void;
26
+ readFile(path: string | URL, options: null, callback: (err: Error | null, data: Uint8Array) => void): void;
27
+ stat(path: string | URL, callback: (err: Error | null, stats: Stats) => void): void;
28
+ mkdir(path: string | URL, options: {
29
+ recursive: true;
30
+ }, callback: (err: Error | null, path?: string) => void): void;
31
+ writeFile(path: string | URL, data: string | Uint8Array, callback: (err: Error | null) => void): void;
32
+ }
33
+ export declare const isFilesystem: (x: unknown) => x is Filesystem;
34
+ export declare const isIterable: <T>(x: unknown) => x is Iterable<T>;
35
+ export declare const isAsyncIterable: <T>(x: unknown) => x is AsyncIterable<T>;
36
+ export declare const getType: (x: unknown) => string;
37
+ export {};
package/cjs/helpers.js ADDED
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getType = exports.isAsyncIterable = exports.isIterable = exports.isFilesystem = void 0;
4
+ const isFilesystem = (x) => !!x && typeof x === 'object' && 'stat' in x && 'mkdir' in x && 'readFile' in x && 'writeFile' in x;
5
+ exports.isFilesystem = isFilesystem;
6
+ const isIterable = (x) => !!x && typeof x === 'object' && Symbol.iterator in x && typeof x[Symbol.iterator] === 'function';
7
+ exports.isIterable = isIterable;
8
+ const isAsyncIterable = (x) => !!x && typeof x === 'object' && Symbol.asyncIterator in x && typeof x[Symbol.asyncIterator] === 'function';
9
+ exports.isAsyncIterable = isAsyncIterable;
10
+ const getType = (x) => typeof x === 'object' ? (x ? (Array.isArray(x) ? 'array' : 'object') : 'null') : typeof x;
11
+ exports.getType = getType;
package/cjs/index.d.ts CHANGED
@@ -1,15 +1,4 @@
1
- export declare namespace staticPages {
2
- type Route<R extends Record<string, unknown> = Record<string, unknown>, W extends Record<string, unknown> = Record<string, unknown>> = {
3
- from: Iterable<R> | AsyncIterable<R>;
4
- to(data: IteratorResult<W>): void | Promise<void>;
5
- controller?(data: R): void | W | W[] | Promise<void | W | W[]>;
6
- };
7
- }
8
- export declare function staticPages<R1 extends Record<string, unknown> = Record<string, unknown>, W1 extends Record<string, unknown> = Record<string, unknown>>(...routes: [staticPages.Route<R1, W1>]): Promise<void>;
9
- export declare function staticPages<R1 extends Record<string, unknown> = Record<string, unknown>, W1 extends Record<string, unknown> = Record<string, unknown>, R2 extends Record<string, unknown> = Record<string, unknown>, W2 extends Record<string, unknown> = Record<string, unknown>>(...routes: [staticPages.Route<R1, W1>, staticPages.Route<R2, W2>]): Promise<void>;
10
- export declare function staticPages<R1 extends Record<string, unknown> = Record<string, unknown>, W1 extends Record<string, unknown> = Record<string, unknown>, R2 extends Record<string, unknown> = Record<string, unknown>, W2 extends Record<string, unknown> = Record<string, unknown>, R3 extends Record<string, unknown> = Record<string, unknown>, W3 extends Record<string, unknown> = Record<string, unknown>>(...routes: [staticPages.Route<R1, W1>, staticPages.Route<R2, W2>, staticPages.Route<R3, W3>]): Promise<void>;
11
- export declare function staticPages<R1 extends Record<string, unknown> = Record<string, unknown>, W1 extends Record<string, unknown> = Record<string, unknown>, R2 extends Record<string, unknown> = Record<string, unknown>, W2 extends Record<string, unknown> = Record<string, unknown>, R3 extends Record<string, unknown> = Record<string, unknown>, W3 extends Record<string, unknown> = Record<string, unknown>, R4 extends Record<string, unknown> = Record<string, unknown>, W4 extends Record<string, unknown> = Record<string, unknown>>(...routes: [staticPages.Route<R1, W1>, staticPages.Route<R2, W2>, staticPages.Route<R3, W3>, staticPages.Route<R4, W4>]): Promise<void>;
12
- export declare function staticPages<R1 extends Record<string, unknown> = Record<string, unknown>, W1 extends Record<string, unknown> = Record<string, unknown>, R2 extends Record<string, unknown> = Record<string, unknown>, W2 extends Record<string, unknown> = Record<string, unknown>, R3 extends Record<string, unknown> = Record<string, unknown>, W3 extends Record<string, unknown> = Record<string, unknown>, R4 extends Record<string, unknown> = Record<string, unknown>, W4 extends Record<string, unknown> = Record<string, unknown>, R5 extends Record<string, unknown> = Record<string, unknown>, W5 extends Record<string, unknown> = Record<string, unknown>>(...routes: [staticPages.Route<R1, W1>, staticPages.Route<R2, W2>, staticPages.Route<R3, W3>, staticPages.Route<R4, W4>, staticPages.Route<R5, W5>]): Promise<void>;
13
- export declare function staticPages<R1 extends Record<string, unknown> = Record<string, unknown>, W1 extends Record<string, unknown> = Record<string, unknown>, R2 extends Record<string, unknown> = Record<string, unknown>, W2 extends Record<string, unknown> = Record<string, unknown>, R3 extends Record<string, unknown> = Record<string, unknown>, W3 extends Record<string, unknown> = Record<string, unknown>, R4 extends Record<string, unknown> = Record<string, unknown>, W4 extends Record<string, unknown> = Record<string, unknown>, R5 extends Record<string, unknown> = Record<string, unknown>, W5 extends Record<string, unknown> = Record<string, unknown>, R6 extends Record<string, unknown> = Record<string, unknown>, W6 extends Record<string, unknown> = Record<string, unknown>>(...routes: [staticPages.Route<R1, W1>, staticPages.Route<R2, W2>, staticPages.Route<R3, W3>, staticPages.Route<R4, W4>, staticPages.Route<R5, W5>, staticPages.Route<R6, W6>]): Promise<void>;
14
- export declare function staticPages(...routes: staticPages.Route[]): Promise<void>;
15
- export default staticPages;
1
+ export { staticPages, staticPages as default } from './static-pages.js';
2
+ export { createReader } from './create-reader.js';
3
+ export { createWriter } from './create-writer.js';
4
+ export type { Filesystem } from './helpers.js';
package/cjs/index.js CHANGED
@@ -1,36 +1,10 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.staticPages = void 0;
4
- const getType = (x) => typeof x === 'object' ? (x ? 'object' : 'null') : typeof x;
5
- const isIterable = (x) => typeof (x === null || x === void 0 ? void 0 : x[Symbol.iterator]) === 'function';
6
- const isAsyncIterable = (x) => typeof (x === null || x === void 0 ? void 0 : x[Symbol.asyncIterator]) === 'function';
7
- async function staticPages(...routes) {
8
- for (const route of routes) {
9
- if (typeof route !== 'object' || !route)
10
- throw new Error(`Argument type mismatch, expected 'object', got '${getType(route)}'.`);
11
- const { from, to, controller } = route;
12
- if (!isIterable(from) && !isAsyncIterable(from))
13
- throw new Error('Argument type mismatch, \'from\' exptects \'iterable\' or \'asyncIterable\'.');
14
- if (typeof to !== 'function')
15
- throw new Error(`Argument type mismatch, 'to' expects 'function', got '${getType(to)}'.`);
16
- if (typeof controller !== 'undefined' && typeof controller !== 'function')
17
- throw new Error(`Argument type mismatch, 'controller' expects 'function', got '${getType(controller)}'.`);
18
- const isController = typeof controller === 'function';
19
- for await (const data of from) {
20
- const results = isController ? await controller(data) : data;
21
- if (typeof results === 'object' && results) {
22
- if (Array.isArray(results)) {
23
- for (const result of results) {
24
- await to({ value: result });
25
- }
26
- }
27
- else {
28
- await to({ value: results });
29
- }
30
- }
31
- }
32
- await to({ done: true, value: undefined });
33
- }
34
- }
35
- exports.staticPages = staticPages;
36
- exports.default = staticPages;
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createWriter = exports.createReader = exports.default = exports.staticPages = void 0;
4
+ var static_pages_js_1 = require("./static-pages.js");
5
+ Object.defineProperty(exports, "staticPages", { enumerable: true, get: function () { return static_pages_js_1.staticPages; } });
6
+ Object.defineProperty(exports, "default", { enumerable: true, get: function () { return static_pages_js_1.staticPages; } });
7
+ var create_reader_js_1 = require("./create-reader.js");
8
+ Object.defineProperty(exports, "createReader", { enumerable: true, get: function () { return create_reader_js_1.createReader; } });
9
+ var create_writer_js_1 = require("./create-writer.js");
10
+ Object.defineProperty(exports, "createWriter", { enumerable: true, get: function () { return create_writer_js_1.createWriter; } });