@eighty4/dank 0.0.4-1 → 0.0.4-3

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/lib_js/dank.js CHANGED
@@ -1,123 +1,6 @@
1
- export async function defineConfig(c) {
2
- if (c.port !== null && typeof c.port !== 'undefined') {
3
- if (typeof c.port !== 'number') {
4
- throw Error('DankConfig.port must be a number');
5
- }
6
- }
7
- if (c.previewPort !== null && typeof c.previewPort !== 'undefined') {
8
- if (typeof c.previewPort !== 'number') {
9
- throw Error('DankConfig.previewPort must be a number');
10
- }
11
- }
12
- validatePages(c.pages);
13
- validateDevServices(c.services);
14
- validateEsbuildConfig(c.esbuild);
15
- normalizePagePaths(c.pages);
16
- return c;
17
- }
18
- function validateEsbuildConfig(esbuild) {
19
- if (esbuild?.loaders !== null && typeof esbuild?.loaders !== 'undefined') {
20
- if (typeof esbuild.loaders !== 'object') {
21
- throw Error('DankConfig.esbuild.loaders must be a map of extensions to esbuild loaders');
22
- }
23
- else {
24
- for (const [ext, loader] of Object.entries(esbuild.loaders)) {
25
- if (typeof loader !== 'string') {
26
- throw Error(`DankConfig.esbuild.loaders['${ext}'] must be a string of a loader name`);
27
- }
28
- }
29
- }
30
- }
31
- if (esbuild?.plugins !== null && typeof esbuild?.plugins !== 'undefined') {
32
- if (!Array.isArray(esbuild.plugins)) {
33
- throw Error('DankConfig.esbuild.plugins must be an array of esbuild plugins');
34
- }
35
- }
36
- if (esbuild?.port !== null && typeof esbuild?.port !== 'undefined') {
37
- if (typeof esbuild.port !== 'number') {
38
- throw Error('DankConfig.esbuild.port must be a number');
39
- }
40
- }
41
- }
42
- function validatePages(pages) {
43
- if (pages === null ||
44
- typeof pages === 'undefined' ||
45
- Object.keys(pages).length === 0) {
46
- throw Error('DankConfig.pages is required');
47
- }
48
- for (const [urlPath, mapping] of Object.entries(pages)) {
49
- if (typeof mapping === 'string' && mapping.endsWith('.html')) {
50
- continue;
51
- }
52
- if (typeof mapping === 'object') {
53
- validatePageMapping(urlPath, mapping);
54
- continue;
55
- }
56
- throw Error(`DankConfig.pages['${urlPath}'] must configure an html file`);
57
- }
58
- }
59
- function validatePageMapping(urlPath, mapping) {
60
- if (mapping.webpage === null ||
61
- typeof mapping.webpage !== 'string' ||
62
- !mapping.webpage.endsWith('.html')) {
63
- throw Error(`DankConfig.pages['${urlPath}'].webpage must configure an html file`);
64
- }
65
- if (mapping.pattern === null || typeof mapping.pattern === 'undefined') {
66
- return;
67
- }
68
- if (typeof mapping.pattern === 'object' &&
69
- mapping.pattern.constructor.name === 'RegExp') {
70
- return;
71
- }
72
- throw Error(`DankConfig.pages['${urlPath}'].pattern must be a RegExp`);
73
- }
74
- function validateDevServices(services) {
75
- if (services === null || typeof services === 'undefined') {
76
- return;
77
- }
78
- if (!Array.isArray(services)) {
79
- throw Error(`DankConfig.services must be an array`);
80
- }
81
- for (let i = 0; i < services.length; i++) {
82
- const s = services[i];
83
- if (s.command === null || typeof s.command === 'undefined') {
84
- throw Error(`DankConfig.services[${i}].command is required`);
85
- }
86
- else if (typeof s.command !== 'string' || s.command.length === 0) {
87
- throw Error(`DankConfig.services[${i}].command must be a non-empty string`);
88
- }
89
- if (s.cwd !== null && typeof s.cwd !== 'undefined') {
90
- if (typeof s.cwd !== 'string' || s.cwd.trim().length === 0) {
91
- throw Error(`DankConfig.services[${i}].cwd must be a non-empty string`);
92
- }
93
- }
94
- if (s.env !== null && typeof s.env !== 'undefined') {
95
- if (typeof s.env !== 'object') {
96
- throw Error(`DankConfig.services[${i}].env must be an env variable map`);
97
- }
98
- for (const [k, v] of Object.entries(s.env)) {
99
- if (typeof v !== 'string') {
100
- throw Error(`DankConfig.services[${i}].env[${k}] must be a string`);
101
- }
102
- }
103
- }
104
- if (s.http !== null && typeof s.http !== 'undefined') {
105
- if (typeof s.http.port !== 'number') {
106
- throw Error(`DankConfig.services[${i}].http.port must be a number`);
107
- }
108
- }
109
- }
110
- }
111
- function normalizePagePaths(pages) {
112
- for (const [pageUrl, mapping] of Object.entries(pages)) {
113
- if (typeof mapping === 'string') {
114
- pages[pageUrl] = normalizePagePath(mapping);
115
- }
116
- else {
117
- mapping.webpage = normalizePagePath(mapping.webpage);
118
- }
119
- }
120
- }
121
- function normalizePagePath(p) {
122
- return p.replace(/^\.\//, '');
1
+ function defineConfig(config) {
2
+ return config;
123
3
  }
4
+ export {
5
+ defineConfig
6
+ };
package/lib_js/define.js CHANGED
@@ -1,6 +1,9 @@
1
- export function createGlobalDefinitions(build) {
2
- return {
3
- 'dank.IS_DEV': JSON.stringify(!build.production),
4
- 'dank.IS_PROD': JSON.stringify(build.production),
5
- };
1
+ function createGlobalDefinitions(c) {
2
+ return {
3
+ "dank.IS_DEV": JSON.stringify(!c.flags.production),
4
+ "dank.IS_PROD": JSON.stringify(c.flags.production)
5
+ };
6
6
  }
7
+ export {
8
+ createGlobalDefinitions
9
+ };
package/lib_js/dirs.js ADDED
@@ -0,0 +1,61 @@
1
+ import { realpath } from "node:fs/promises";
2
+ import { dirname, isAbsolute, join, resolve } from "node:path";
3
+ import { cwd } from "node:process";
4
+ async function defaultProjectDirs(projectRootAbs) {
5
+ if (!projectRootAbs) {
6
+ projectRootAbs = cwd();
7
+ } else if (!isAbsolute(projectRootAbs)) {
8
+ throw Error();
9
+ }
10
+ const projectResolved = await realpath(projectRootAbs);
11
+ const pages = "pages";
12
+ const pagesResolved = join(projectResolved, pages);
13
+ return Object.freeze({
14
+ buildRoot: "build",
15
+ buildDist: join("build", "dist"),
16
+ buildWatch: join("build", "watch"),
17
+ pages,
18
+ pagesResolved,
19
+ projectResolved,
20
+ projectRootAbs,
21
+ public: "public"
22
+ });
23
+ }
24
+ class Resolver {
25
+ #dirs;
26
+ constructor(dirs) {
27
+ this.#dirs = dirs;
28
+ }
29
+ // cross platform safe absolute path resolution from pages dir
30
+ absPagesPath(...p) {
31
+ return join(this.#dirs.projectRootAbs, this.#dirs.pages, ...p);
32
+ }
33
+ // cross platform safe absolute path resolution from project root
34
+ absProjectPath(...p) {
35
+ return join(this.#dirs.projectRootAbs, ...p);
36
+ }
37
+ // `p` is expected to be a relative path resolvable from the project dir
38
+ isProjectSubpathInPagesDir(p) {
39
+ return resolve(join(this.#dirs.projectResolved, p)).startsWith(this.#dirs.pagesResolved);
40
+ }
41
+ // `p` is expected to be a relative path resolvable from the pages dir
42
+ isPagesSubpathInPagesDir(p) {
43
+ return this.isProjectSubpathInPagesDir(join(this.#dirs.pages, p));
44
+ }
45
+ // resolve a pages subpath from a resource within the pages directory by a relative href
46
+ // `from` is expected to be a pages resource fs path starting with `pages/` and ending with filename
47
+ // the result will be a pages subpath and will not have the pages dir prefix
48
+ // returns 'outofbounds' if the relative path does not resolve to a file within the pages dir
49
+ resolveHrefInPagesDir(from, href) {
50
+ const p = join(dirname(from), href);
51
+ if (this.isProjectSubpathInPagesDir(p)) {
52
+ return p;
53
+ } else {
54
+ return "outofbounds";
55
+ }
56
+ }
57
+ }
58
+ export {
59
+ Resolver,
60
+ defaultProjectDirs
61
+ };
@@ -0,0 +1,9 @@
1
+ class DankError extends Error {
2
+ constructor(message, cause) {
3
+ super(message, { cause });
4
+ this.name = "DankError";
5
+ }
6
+ }
7
+ export {
8
+ DankError
9
+ };
package/lib_js/esbuild.js CHANGED
@@ -1,186 +1,174 @@
1
- import { readFile } from 'node:fs/promises';
2
- import esbuild, {} from 'esbuild';
3
- export async function esbuildDevContext(b, r, define, entryPoints, c) {
4
- return await esbuild.context({
5
- define,
6
- entryNames: '[dir]/[name]',
7
- entryPoints: mapEntryPointPaths(entryPoints),
8
- outdir: b.dirs.buildWatch,
9
- ...commonBuildOptions(b, r, c),
10
- splitting: false,
11
- write: false,
12
- });
1
+ import { readFile } from "node:fs/promises";
2
+ import esbuild, {} from "esbuild";
3
+ async function esbuildDevContext(r, define, entryPoints) {
4
+ return await esbuild.context({
5
+ define,
6
+ entryNames: "[dir]/[name]",
7
+ entryPoints: mapEntryPointPaths(entryPoints),
8
+ outdir: r.config.dirs.buildWatch,
9
+ ...commonBuildOptions(r),
10
+ splitting: false,
11
+ write: false
12
+ });
13
13
  }
14
- export async function esbuildWebpages(b, r, define, entryPoints, c) {
15
- try {
16
- await esbuild.build({
17
- define,
18
- entryNames: '[dir]/[name]-[hash]',
19
- entryPoints: mapEntryPointPaths(entryPoints),
20
- outdir: b.dirs.buildDist,
21
- ...commonBuildOptions(b, r, c),
22
- });
23
- }
24
- catch (ignore) {
25
- process.exit(1);
26
- }
14
+ async function esbuildWebpages(r, define, entryPoints) {
15
+ try {
16
+ await esbuild.build({
17
+ define,
18
+ entryNames: "[dir]/[name]-[hash]",
19
+ entryPoints: mapEntryPointPaths(entryPoints),
20
+ outdir: r.config.dirs.buildDist,
21
+ ...commonBuildOptions(r)
22
+ });
23
+ } catch (ignore) {
24
+ process.exit(1);
25
+ }
27
26
  }
28
- export async function esbuildWorkers(b, r, define, entryPoints, c) {
29
- try {
30
- await esbuild.build({
31
- define,
32
- entryNames: '[dir]/[name]-[hash]',
33
- entryPoints: mapEntryPointPaths(entryPoints),
34
- outdir: b.dirs.buildDist,
35
- ...commonBuildOptions(b, r, c),
36
- splitting: false,
37
- metafile: true,
38
- write: true,
39
- assetNames: 'assets/[name]-[hash]',
40
- });
41
- }
42
- catch (ignore) {
43
- process.exit(1);
44
- }
27
+ async function esbuildWorkers(r, define, entryPoints) {
28
+ try {
29
+ await esbuild.build({
30
+ define,
31
+ entryNames: "[dir]/[name]-[hash]",
32
+ entryPoints: mapEntryPointPaths(entryPoints),
33
+ outdir: r.config.dirs.buildDist,
34
+ ...commonBuildOptions(r),
35
+ splitting: false,
36
+ metafile: true,
37
+ write: true,
38
+ assetNames: "assets/[name]-[hash]"
39
+ });
40
+ } catch (ignore) {
41
+ process.exit(1);
42
+ }
45
43
  }
46
- function commonBuildOptions(b, r, c) {
47
- const p = workersPlugin(r.buildRegistry());
48
- return {
49
- absWorkingDir: b.dirs.projectRootAbs,
50
- assetNames: 'assets/[name]-[hash]',
51
- bundle: true,
52
- format: 'esm',
53
- loader: c?.loaders || defaultLoaders(),
54
- metafile: true,
55
- minify: b.minify,
56
- platform: 'browser',
57
- plugins: c?.plugins?.length ? [p, ...c.plugins] : [p],
58
- splitting: true,
59
- treeShaking: true,
60
- write: true,
61
- };
44
+ function commonBuildOptions(r) {
45
+ const p = workersPlugin(r.buildRegistry());
46
+ return {
47
+ assetNames: "assets/[name]-[hash]",
48
+ bundle: true,
49
+ format: "esm",
50
+ loader: r.config.esbuild?.loaders || defaultLoaders(),
51
+ metafile: true,
52
+ minify: r.config.flags.minify,
53
+ platform: "browser",
54
+ plugins: r.config.esbuild?.plugins?.length ? [p, ...r.config.esbuild?.plugins] : [p],
55
+ splitting: true,
56
+ treeShaking: true,
57
+ write: true
58
+ };
62
59
  }
63
60
  function defaultLoaders() {
64
- return {
65
- '.woff': 'file',
66
- '.woff2': 'file',
67
- };
61
+ return {
62
+ ".woff": "file",
63
+ ".woff2": "file"
64
+ };
68
65
  }
69
- // esbuild will append the .js or .css to output filenames
70
- // keeping extension on entryPoints data for consistency
71
- // and only trimming when creating esbuild opts
72
66
  function mapEntryPointPaths(entryPoints) {
73
- return entryPoints.map(entryPoint => {
74
- return {
75
- in: entryPoint.in,
76
- out: entryPoint.out.replace(/\.(tsx?|jsx?|css)$/, ''),
77
- };
78
- });
67
+ return entryPoints.map((entryPoint) => {
68
+ return {
69
+ in: entryPoint.in,
70
+ out: entryPoint.out.replace(/\.(tsx?|jsx?|css)$/, "")
71
+ };
72
+ });
79
73
  }
80
74
  const WORKER_CTOR_REGEX = /new(?:\s|\r?\n)+(?<ctor>(?:Shared)?Worker)(?:\s|\r?\n)*\((?:\s|\r?\n)*(?<url>.*?)(?:\s|\r?\n)*(?<end>[\),])/g;
81
75
  const WORKER_URL_REGEX = /^('.*'|".*")$/;
82
- export function workersPlugin(r) {
83
- return {
84
- name: '@eighty4/dank/esbuild/workers',
85
- setup(build) {
86
- if (!build.initialOptions.absWorkingDir)
87
- throw TypeError('plugin requires absWorkingDir');
88
- if (!build.initialOptions.metafile)
89
- throw TypeError('plugin requires metafile');
90
- const { absWorkingDir } = build.initialOptions;
91
- build.onLoad({ filter: /\.(t|m?j)s$/ }, async (args) => {
92
- let contents = await readFile(args.path, 'utf8');
93
- let offset = 0;
94
- let errors = undefined;
95
- for (const workerCtorMatch of contents.matchAll(WORKER_CTOR_REGEX)) {
96
- if (!WORKER_URL_REGEX.test(workerCtorMatch.groups.url)) {
97
- if (!errors)
98
- errors = [];
99
- errors.push(invalidWorkerUrlCtorArg(locationFromMatch(args, contents, workerCtorMatch), workerCtorMatch));
100
- continue;
101
- }
102
- if (isIndexCommented(contents, workerCtorMatch.index)) {
103
- continue;
104
- }
105
- const clientScript = args.path
106
- .replace(absWorkingDir, '')
107
- .substring(1);
108
- const workerUrl = workerCtorMatch.groups.url.substring(1, workerCtorMatch.groups.url.length - 1);
109
- const workerEntryPoint = r.resolver.resolveHrefInPagesDir(clientScript, workerUrl);
110
- if (workerEntryPoint === 'outofbounds') {
111
- if (!errors)
112
- errors = [];
113
- errors.push(outofboundsWorkerUrlCtorArg(locationFromMatch(args, contents, workerCtorMatch), workerCtorMatch));
114
- continue;
115
- }
116
- const workerUrlPlaceholder = workerEntryPoint
117
- .replace(/^pages/, '')
118
- .replace(/\.(t|m?j)s$/, '.js');
119
- const workerCtorReplacement = `new ${workerCtorMatch.groups.ctor}('${workerUrlPlaceholder}'${workerCtorMatch.groups.end}`;
120
- contents =
121
- contents.substring(0, workerCtorMatch.index + offset) +
122
- workerCtorReplacement +
123
- contents.substring(workerCtorMatch.index +
124
- workerCtorMatch[0].length +
125
- offset);
126
- offset +=
127
- workerCtorReplacement.length - workerCtorMatch[0].length;
128
- r.addWorker({
129
- clientScript,
130
- workerEntryPoint,
131
- workerUrl,
132
- workerUrlPlaceholder,
133
- });
134
- }
135
- const loader = args.path.endsWith('ts') ? 'ts' : 'js';
136
- return { contents, errors, loader };
137
- });
138
- build.onEnd((result) => {
139
- if (result.metafile) {
140
- r.completeBuild(result);
141
- }
142
- });
143
- },
144
- };
76
+ function workersPlugin(r) {
77
+ return {
78
+ name: "@eighty4/dank/esbuild/workers",
79
+ setup(build) {
80
+ if (!build.initialOptions.metafile)
81
+ throw TypeError("plugin requires metafile");
82
+ build.onLoad({ filter: /\.(t|m?j)s$/ }, async (args) => {
83
+ let contents = await readFile(args.path, "utf8");
84
+ let offset = 0;
85
+ let errors = void 0;
86
+ for (const workerCtorMatch of contents.matchAll(WORKER_CTOR_REGEX)) {
87
+ if (!WORKER_URL_REGEX.test(workerCtorMatch.groups.url)) {
88
+ if (!errors)
89
+ errors = [];
90
+ errors.push(invalidWorkerUrlCtorArg(locationFromMatch(args, contents, workerCtorMatch), workerCtorMatch));
91
+ continue;
92
+ }
93
+ if (isIndexCommented(contents, workerCtorMatch.index)) {
94
+ continue;
95
+ }
96
+ const clientScript = args.path.replace(r.dirs.projectResolved, "").substring(1);
97
+ const workerUrl = workerCtorMatch.groups.url.substring(1, workerCtorMatch.groups.url.length - 1);
98
+ const workerEntryPoint = r.resolver.resolveHrefInPagesDir(clientScript, workerUrl);
99
+ if (workerEntryPoint === "outofbounds") {
100
+ if (!errors)
101
+ errors = [];
102
+ errors.push(outofboundsWorkerUrlCtorArg(locationFromMatch(args, contents, workerCtorMatch), workerCtorMatch));
103
+ continue;
104
+ }
105
+ const workerCtor = workerCtorMatch.groups.ctor;
106
+ const workerUrlPlaceholder = workerEntryPoint.replace(/^pages/, "").replace(/\.(t|m?j)s$/, ".js");
107
+ const workerCtorReplacement = `new ${workerCtor}('${workerUrlPlaceholder}'${workerCtorMatch.groups.end}`;
108
+ contents = contents.substring(0, workerCtorMatch.index + offset) + workerCtorReplacement + contents.substring(workerCtorMatch.index + workerCtorMatch[0].length + offset);
109
+ offset += workerCtorReplacement.length - workerCtorMatch[0].length;
110
+ r.addWorker({
111
+ clientScript,
112
+ workerEntryPoint,
113
+ workerCtor,
114
+ workerUrl,
115
+ workerUrlPlaceholder
116
+ });
117
+ }
118
+ const loader = args.path.endsWith("ts") ? "ts" : "js";
119
+ return { contents, errors, loader };
120
+ });
121
+ build.onEnd((result) => {
122
+ if (result.metafile) {
123
+ r.completeBuild(result);
124
+ }
125
+ });
126
+ }
127
+ };
145
128
  }
146
129
  function isIndexCommented(contents, index) {
147
- const preamble = contents.substring(0, index);
148
- const lineIndex = preamble.lastIndexOf('\n') || 0;
149
- const lineCommented = /\/\//.test(preamble.substring(lineIndex));
150
- if (lineCommented) {
151
- return true;
152
- }
153
- const blockCommentIndex = preamble.lastIndexOf('/*');
154
- const blockCommented = blockCommentIndex !== -1 &&
155
- preamble.substring(blockCommentIndex).indexOf('*/') === -1;
156
- return blockCommented;
130
+ const preamble = contents.substring(0, index);
131
+ const lineIndex = preamble.lastIndexOf("\n") || 0;
132
+ const lineCommented = /\/\//.test(preamble.substring(lineIndex));
133
+ if (lineCommented) {
134
+ return true;
135
+ }
136
+ const blockCommentIndex = preamble.lastIndexOf("/*");
137
+ const blockCommented = blockCommentIndex !== -1 && preamble.substring(blockCommentIndex).indexOf("*/") === -1;
138
+ return blockCommented;
157
139
  }
158
140
  function locationFromMatch(args, contents, match) {
159
- const preamble = contents.substring(0, match.index);
160
- const line = preamble.match(/\n/g)?.length || 0;
161
- let lineIndex = preamble.lastIndexOf('\n');
162
- lineIndex = lineIndex === -1 ? 0 : lineIndex + 1;
163
- const column = preamble.length - lineIndex;
164
- const lineText = contents.substring(lineIndex, contents.indexOf('\n', lineIndex) || contents.length);
165
- return {
166
- lineText,
167
- line,
168
- column,
169
- file: args.path,
170
- length: match[0].length,
171
- };
141
+ const preamble = contents.substring(0, match.index);
142
+ const line = preamble.match(/\n/g)?.length || 0;
143
+ let lineIndex = preamble.lastIndexOf("\n");
144
+ lineIndex = lineIndex === -1 ? 0 : lineIndex + 1;
145
+ const column = preamble.length - lineIndex;
146
+ const lineText = contents.substring(lineIndex, contents.indexOf("\n", lineIndex) || contents.length);
147
+ return {
148
+ lineText,
149
+ line,
150
+ column,
151
+ file: args.path,
152
+ length: match[0].length
153
+ };
172
154
  }
173
155
  function outofboundsWorkerUrlCtorArg(location, workerCtorMatch) {
174
- return {
175
- id: 'worker-url-outofbounds',
176
- text: `The ${workerCtorMatch.groups.ctor} constructor URL arg \`${workerCtorMatch.groups.url}\` cannot resolve to a path outside of the pages directory`,
177
- location,
178
- };
156
+ return {
157
+ id: "worker-url-outofbounds",
158
+ text: `The ${workerCtorMatch.groups.ctor} constructor URL arg \`${workerCtorMatch.groups.url}\` cannot resolve to a path outside of the pages directory`,
159
+ location
160
+ };
179
161
  }
180
162
  function invalidWorkerUrlCtorArg(location, workerCtorMatch) {
181
- return {
182
- id: 'worker-url-unresolvable',
183
- text: `The ${workerCtorMatch.groups.ctor} constructor URL arg \`${workerCtorMatch.groups.url}\` must be a relative module path`,
184
- location,
185
- };
163
+ return {
164
+ id: "worker-url-unresolvable",
165
+ text: `The ${workerCtorMatch.groups.ctor} constructor URL arg \`${workerCtorMatch.groups.url}\` must be a relative module path`,
166
+ location
167
+ };
186
168
  }
169
+ export {
170
+ esbuildDevContext,
171
+ esbuildWebpages,
172
+ esbuildWorkers,
173
+ workersPlugin
174
+ };