@tileflow/dev 0.1.0-alpha.7 → 0.1.0-alpha.8
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 +13 -2
- package/dist/index.d.ts +11 -1
- package/dist/index.js +267 -34
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -2,12 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
Node utilities used by the Tileflow CLI and build integrations.
|
|
4
4
|
|
|
5
|
+
Most apps should use `@tileflow/cli` or `@tileflow/vite` directly. Use this package when you are
|
|
6
|
+
building a custom dev server, bundler plugin, or deployment adapter that needs to load and compile a
|
|
7
|
+
`tileflow.config.ts`.
|
|
8
|
+
|
|
5
9
|
```ts
|
|
6
|
-
import {
|
|
10
|
+
import {createTileflowBuildArtifacts} from '@tileflow/dev';
|
|
7
11
|
|
|
8
12
|
const artifacts = await createTileflowBuildArtifacts({
|
|
9
|
-
config:
|
|
13
|
+
config: 'tileflow.config.ts',
|
|
10
14
|
});
|
|
11
15
|
```
|
|
12
16
|
|
|
17
|
+
If a map uses `icons: './icons/brand'`, build artifacts include the generated
|
|
18
|
+
MapLibre `sprite.json`, `sprite.png`, `sprite@2x.json`, and `sprite@2x.png`
|
|
19
|
+
assets alongside the styles.
|
|
20
|
+
|
|
21
|
+
This package is public, but it is an integration layer. Map styling primitives live in
|
|
22
|
+
`@tileflow/core`; React rendering lives in `@tileflow/react`.
|
|
23
|
+
|
|
13
24
|
Docs: https://tileflow.dev/docs
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
import { TileflowManifest, MapLibreStyle, ValidationMessage, TileflowProjectConfig, TileflowStyleOptions } from '@tileflow/core';
|
|
2
2
|
|
|
3
|
+
type TileflowBuildAsset = {
|
|
4
|
+
contentType: string;
|
|
5
|
+
fileName: string;
|
|
6
|
+
source: string | Uint8Array;
|
|
7
|
+
};
|
|
8
|
+
|
|
3
9
|
declare const defaultTileflowApiUrl = "https://api.tileflow.dev";
|
|
4
10
|
declare const defaultTileflowConfigPath = "tileflow.config.ts";
|
|
5
11
|
declare const defaultTileflowManifestPath = "public/tileflow/manifest.json";
|
|
@@ -21,14 +27,17 @@ type TileflowDevRequestHandlerOptions = {
|
|
|
21
27
|
tileBaseUrl?: string;
|
|
22
28
|
};
|
|
23
29
|
type TileflowBuildArtifactsOptions = {
|
|
30
|
+
assetBaseUrl?: string;
|
|
24
31
|
config?: string;
|
|
25
32
|
cwd?: string;
|
|
26
33
|
styleBaseUrl?: string;
|
|
27
34
|
tileBaseUrl?: string;
|
|
28
35
|
};
|
|
29
36
|
type TileflowBuildArtifacts = {
|
|
37
|
+
assets: TileflowBuildAsset[];
|
|
30
38
|
manifest: TileflowManifest;
|
|
31
39
|
styles: Record<string, MapLibreStyle>;
|
|
40
|
+
watchPaths: string[];
|
|
32
41
|
};
|
|
33
42
|
type WriteTileflowBuildArtifactsOptions = TileflowBuildArtifactsOptions & {
|
|
34
43
|
outDir: string;
|
|
@@ -47,8 +56,9 @@ declare function getTileflowStyleMapName(path: string): string;
|
|
|
47
56
|
declare function createTileflowManifest(project: TileflowProjectConfig, options?: TileflowManifestOptions): TileflowManifest;
|
|
48
57
|
declare function createTileflowStyle(project: TileflowProjectConfig, mapName: string, options?: TileflowStyleOptions): MapLibreStyle;
|
|
49
58
|
declare function createTileflowBuildArtifacts(options?: TileflowBuildArtifactsOptions): Promise<TileflowBuildArtifacts>;
|
|
59
|
+
declare function getTileflowWatchPaths(options?: Pick<TileflowBuildArtifactsOptions, 'config' | 'cwd'>): Promise<string[]>;
|
|
50
60
|
declare function writeTileflowBuildArtifacts(options: WriteTileflowBuildArtifactsOptions): Promise<TileflowBuildArtifacts>;
|
|
51
61
|
declare function createTileflowDevRequestHandler(options?: TileflowDevRequestHandlerOptions): (request: Request) => Promise<Response>;
|
|
52
62
|
declare function normalizeTileflowBasePath(value?: string): string;
|
|
53
63
|
|
|
54
|
-
export { type LoadTileflowConfigOptions, type TileflowBuildArtifacts, type TileflowBuildArtifactsOptions, type TileflowDevRequestHandlerOptions, type TileflowManifestOptions, TileflowValidationError, type WriteTileflowBuildArtifactsOptions, asTileflowProjectConfig, assertValidTileflowConfig, createTileflowBuildArtifacts, createTileflowDevRequestHandler, createTileflowManifest, createTileflowStyle, defaultTileflowApiUrl, defaultTileflowConfigPath, defaultTileflowManifestPath, defaultTileflowTileset, getFirstTileflowMapName, getTileflowMapNames, getTileflowStyleMapName, loadTileflowConfig, loadValidTileflowConfig, normalizeTileflowBasePath, writeTileflowBuildArtifacts };
|
|
64
|
+
export { type LoadTileflowConfigOptions, type TileflowBuildArtifacts, type TileflowBuildArtifactsOptions, type TileflowDevRequestHandlerOptions, type TileflowManifestOptions, TileflowValidationError, type WriteTileflowBuildArtifactsOptions, asTileflowProjectConfig, assertValidTileflowConfig, createTileflowBuildArtifacts, createTileflowDevRequestHandler, createTileflowManifest, createTileflowStyle, defaultTileflowApiUrl, defaultTileflowConfigPath, defaultTileflowManifestPath, defaultTileflowTileset, getFirstTileflowMapName, getTileflowMapNames, getTileflowStyleMapName, getTileflowWatchPaths, loadTileflowConfig, loadValidTileflowConfig, normalizeTileflowBasePath, writeTileflowBuildArtifacts };
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,196 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
+
import { createJiti } from "jiti";
|
|
3
|
+
import { existsSync as existsSync2 } from "fs";
|
|
4
|
+
import { mkdir, writeFile } from "fs/promises";
|
|
5
|
+
import { dirname, resolve as resolve2 } from "path";
|
|
2
6
|
import {
|
|
3
7
|
createManifest,
|
|
4
8
|
createStyleFromProject,
|
|
5
9
|
validateConfig
|
|
6
10
|
} from "@tileflow/core";
|
|
11
|
+
|
|
12
|
+
// src/icons.ts
|
|
7
13
|
import { existsSync } from "fs";
|
|
8
|
-
import {
|
|
9
|
-
import { resolve } from "path";
|
|
10
|
-
|
|
14
|
+
import { readdir } from "fs/promises";
|
|
15
|
+
import { extname, resolve } from "path";
|
|
16
|
+
var iconFileExtensions = /* @__PURE__ */ new Set([".svg", ".png", ".jpg", ".jpeg", ".webp"]);
|
|
17
|
+
var iconSpriteSize = 24;
|
|
18
|
+
async function prepareTileflowProjectIcons(project, options) {
|
|
19
|
+
const nextProject = cloneProject(project);
|
|
20
|
+
const localSources = getLocalIconSources(project, options.cwd);
|
|
21
|
+
for (const localSource of localSources) {
|
|
22
|
+
const mapConfig = nextProject.maps[localSource.mapName];
|
|
23
|
+
if (!mapConfig) {
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
const iconSet = resolveIconSet(mapConfig.icons, nextProject.icons);
|
|
27
|
+
if (!iconSet) {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
const spriteUrl = joinUrl(options.assetBaseUrl, `icons/${localSource.mapName}/sprite`);
|
|
31
|
+
mapConfig.icons = {
|
|
32
|
+
...iconSet,
|
|
33
|
+
sprite: spriteUrl
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (localSources.length === 0) {
|
|
37
|
+
return {
|
|
38
|
+
assets: [],
|
|
39
|
+
project: nextProject,
|
|
40
|
+
watchPaths: []
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
const assets = (await Promise.all(
|
|
44
|
+
localSources.map(async (source) => buildSpriteAssets(source.sourceDir, source.mapName))
|
|
45
|
+
)).flat();
|
|
46
|
+
return {
|
|
47
|
+
assets,
|
|
48
|
+
project: nextProject,
|
|
49
|
+
watchPaths: localSources.map((source) => source.sourceDir)
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function getTileflowIconWatchPaths(project, cwd) {
|
|
53
|
+
return uniqueStrings(getLocalIconSources(project, cwd).map((source) => source.sourceDir));
|
|
54
|
+
}
|
|
55
|
+
function getLocalIconSources(project, cwd) {
|
|
56
|
+
return Object.entries(project.maps).flatMap(([mapName, mapConfig]) => {
|
|
57
|
+
const iconSet = resolveIconSet(mapConfig.icons, project.icons);
|
|
58
|
+
const sourceDir = resolveLocalIconSource(iconSet, cwd);
|
|
59
|
+
return sourceDir ? [{ mapName, sourceDir }] : [];
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
function resolveLocalIconSource(iconSet, cwd) {
|
|
63
|
+
if (!iconSet?.source) {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
return resolve(cwd, iconSet.source);
|
|
67
|
+
}
|
|
68
|
+
function resolveIconSet(icons, iconSets, path = []) {
|
|
69
|
+
if (!icons) {
|
|
70
|
+
return void 0;
|
|
71
|
+
}
|
|
72
|
+
if (typeof icons === "string") {
|
|
73
|
+
const referenced = iconSets?.[icons];
|
|
74
|
+
if (referenced) {
|
|
75
|
+
if (path.includes(icons)) {
|
|
76
|
+
throw new Error(`Circular Tileflow icon set extends: ${[...path, icons].join(" -> ")}`);
|
|
77
|
+
}
|
|
78
|
+
return resolveIconSet(referenced, iconSets, [...path, icons]);
|
|
79
|
+
}
|
|
80
|
+
return isRemoteSpriteReference(icons) ? { sprite: icons } : { source: icons };
|
|
81
|
+
}
|
|
82
|
+
const extended = icons.extends ? resolveIconSet(icons.extends, iconSets, path) : void 0;
|
|
83
|
+
return {
|
|
84
|
+
...extended,
|
|
85
|
+
...icons,
|
|
86
|
+
mapping: {
|
|
87
|
+
...extended?.mapping,
|
|
88
|
+
...icons.mapping
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
async function buildSpriteAssets(sourceDir, mapName) {
|
|
93
|
+
const icons = await readIconFiles(sourceDir);
|
|
94
|
+
if (icons.length === 0) {
|
|
95
|
+
throw new Error(`No SVG or PNG icons found in ${sourceDir}.`);
|
|
96
|
+
}
|
|
97
|
+
const [sprite, sprite2x] = await Promise.all([
|
|
98
|
+
buildSprite(sourceDir, icons, 1),
|
|
99
|
+
buildSprite(sourceDir, icons, 2)
|
|
100
|
+
]);
|
|
101
|
+
return [
|
|
102
|
+
jsonAsset(`icons/${mapName}/sprite.json`, sprite.index),
|
|
103
|
+
binaryAsset(`icons/${mapName}/sprite.png`, sprite.image, "image/png"),
|
|
104
|
+
jsonAsset(`icons/${mapName}/sprite@2x.json`, sprite2x.index),
|
|
105
|
+
binaryAsset(`icons/${mapName}/sprite@2x.png`, sprite2x.image, "image/png")
|
|
106
|
+
];
|
|
107
|
+
}
|
|
108
|
+
async function readIconFiles(sourceDir) {
|
|
109
|
+
if (!existsSync(sourceDir)) {
|
|
110
|
+
throw new Error(`Icon source not found: ${sourceDir}`);
|
|
111
|
+
}
|
|
112
|
+
const entries = await readdir(sourceDir, { withFileTypes: true });
|
|
113
|
+
return entries.filter((entry) => entry.isFile() && iconFileExtensions.has(extname(entry.name).toLowerCase())).map((entry) => ({
|
|
114
|
+
name: entry.name.slice(0, -extname(entry.name).length),
|
|
115
|
+
path: resolve(sourceDir, entry.name)
|
|
116
|
+
})).sort((left, right) => left.name.localeCompare(right.name));
|
|
117
|
+
}
|
|
118
|
+
async function buildSprite(sourceDir, icons, pixelRatio) {
|
|
119
|
+
const sharp = (await import("sharp")).default;
|
|
120
|
+
const cellSize = iconSpriteSize * pixelRatio;
|
|
121
|
+
const columns = Math.ceil(Math.sqrt(icons.length));
|
|
122
|
+
const rows = Math.ceil(icons.length / columns);
|
|
123
|
+
const width = columns * cellSize;
|
|
124
|
+
const height = rows * cellSize;
|
|
125
|
+
const index = {};
|
|
126
|
+
const composite = await Promise.all(
|
|
127
|
+
icons.map(async (icon, indexNumber) => {
|
|
128
|
+
const column = indexNumber % columns;
|
|
129
|
+
const row = Math.floor(indexNumber / columns);
|
|
130
|
+
const left = column * cellSize;
|
|
131
|
+
const top = row * cellSize;
|
|
132
|
+
index[icon.name] = {
|
|
133
|
+
height: cellSize,
|
|
134
|
+
pixelRatio,
|
|
135
|
+
width: cellSize,
|
|
136
|
+
x: left,
|
|
137
|
+
y: top
|
|
138
|
+
};
|
|
139
|
+
return {
|
|
140
|
+
input: await sharp(icon.path, { density: 72 * pixelRatio }).resize(cellSize, cellSize, {
|
|
141
|
+
background: { alpha: 0, b: 0, g: 0, r: 0 },
|
|
142
|
+
fit: "contain"
|
|
143
|
+
}).png().toBuffer(),
|
|
144
|
+
left,
|
|
145
|
+
top
|
|
146
|
+
};
|
|
147
|
+
})
|
|
148
|
+
);
|
|
149
|
+
const image = await sharp({
|
|
150
|
+
create: {
|
|
151
|
+
background: { alpha: 0, b: 0, g: 0, r: 0 },
|
|
152
|
+
channels: 4,
|
|
153
|
+
height,
|
|
154
|
+
width
|
|
155
|
+
}
|
|
156
|
+
}).composite(composite).png().toBuffer();
|
|
157
|
+
return {
|
|
158
|
+
image,
|
|
159
|
+
index,
|
|
160
|
+
sourceDir
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
function jsonAsset(fileName, value) {
|
|
164
|
+
return {
|
|
165
|
+
contentType: "application/json; charset=utf-8",
|
|
166
|
+
fileName,
|
|
167
|
+
source: `${JSON.stringify(value, null, 2)}
|
|
168
|
+
`
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
function binaryAsset(fileName, source, contentType) {
|
|
172
|
+
return {
|
|
173
|
+
contentType,
|
|
174
|
+
fileName,
|
|
175
|
+
source
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
function cloneProject(project) {
|
|
179
|
+
return JSON.parse(JSON.stringify(project));
|
|
180
|
+
}
|
|
181
|
+
function isRemoteSpriteReference(value) {
|
|
182
|
+
return value.startsWith("http://") || value.startsWith("https://") || value.startsWith("/") || value.startsWith("data:");
|
|
183
|
+
}
|
|
184
|
+
function joinUrl(base, path) {
|
|
185
|
+
const trimmedBase = base.replace(/\/+$/g, "");
|
|
186
|
+
const trimmedPath = path.replace(/^\/+/g, "");
|
|
187
|
+
return trimmedBase ? `${trimmedBase}/${trimmedPath}` : trimmedPath;
|
|
188
|
+
}
|
|
189
|
+
function uniqueStrings(values) {
|
|
190
|
+
return [...new Set(values)];
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// src/index.ts
|
|
11
194
|
var defaultTileflowApiUrl = "https://api.tileflow.dev";
|
|
12
195
|
var defaultTileflowConfigPath = "tileflow.config.ts";
|
|
13
196
|
var defaultTileflowManifestPath = "public/tileflow/manifest.json";
|
|
@@ -22,8 +205,8 @@ var TileflowValidationError = class extends Error {
|
|
|
22
205
|
};
|
|
23
206
|
async function loadTileflowConfig(configPath = defaultTileflowConfigPath, options = {}) {
|
|
24
207
|
const cwd = options.cwd ?? process.cwd();
|
|
25
|
-
const resolvedPath =
|
|
26
|
-
if (!
|
|
208
|
+
const resolvedPath = resolve2(cwd, configPath);
|
|
209
|
+
if (!existsSync2(resolvedPath)) {
|
|
27
210
|
throw new Error(`Config not found: ${configPath}`);
|
|
28
211
|
}
|
|
29
212
|
const jiti = createJiti(import.meta.url, {
|
|
@@ -81,44 +264,60 @@ function createTileflowStyle(project, mapName, options = {}) {
|
|
|
81
264
|
return createStyleFromProject(project, mapName, options);
|
|
82
265
|
}
|
|
83
266
|
async function createTileflowBuildArtifacts(options = {}) {
|
|
84
|
-
const project = await loadValidTileflowConfig(
|
|
85
|
-
options.
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
267
|
+
const project = await loadValidTileflowConfig(options.config ?? defaultTileflowConfigPath, {
|
|
268
|
+
cwd: options.cwd,
|
|
269
|
+
fresh: true
|
|
270
|
+
});
|
|
271
|
+
const prepared = await prepareTileflowProjectIcons(project, {
|
|
272
|
+
assetBaseUrl: resolveAssetBaseUrl(options),
|
|
273
|
+
cwd: options.cwd ?? process.cwd()
|
|
274
|
+
});
|
|
91
275
|
const styleOptions = { tileBaseUrl: options.tileBaseUrl };
|
|
92
276
|
const styles = Object.fromEntries(
|
|
93
|
-
getTileflowMapNames(project).map((mapName) => [
|
|
277
|
+
getTileflowMapNames(prepared.project).map((mapName) => [
|
|
94
278
|
mapName,
|
|
95
|
-
createStyleFromProject(project, mapName, styleOptions)
|
|
279
|
+
createStyleFromProject(prepared.project, mapName, styleOptions)
|
|
96
280
|
])
|
|
97
281
|
);
|
|
98
282
|
return {
|
|
99
|
-
|
|
283
|
+
assets: prepared.assets,
|
|
284
|
+
manifest: createManifest(prepared.project, {
|
|
100
285
|
styleBaseUrl: options.styleBaseUrl,
|
|
101
286
|
tileBaseUrl: options.tileBaseUrl
|
|
102
287
|
}),
|
|
103
|
-
styles
|
|
288
|
+
styles,
|
|
289
|
+
watchPaths: prepared.watchPaths
|
|
104
290
|
};
|
|
105
291
|
}
|
|
292
|
+
async function getTileflowWatchPaths(options = {}) {
|
|
293
|
+
const cwd = options.cwd ?? process.cwd();
|
|
294
|
+
const project = await loadValidTileflowConfig(options.config ?? defaultTileflowConfigPath, {
|
|
295
|
+
cwd,
|
|
296
|
+
fresh: true
|
|
297
|
+
});
|
|
298
|
+
return getTileflowIconWatchPaths(project, cwd);
|
|
299
|
+
}
|
|
106
300
|
async function writeTileflowBuildArtifacts(options) {
|
|
107
301
|
const cwd = options.cwd ?? process.cwd();
|
|
108
|
-
const outDir =
|
|
109
|
-
const stylesDir =
|
|
302
|
+
const outDir = resolve2(cwd, options.outDir);
|
|
303
|
+
const stylesDir = resolve2(outDir, "styles");
|
|
110
304
|
const artifacts = await createTileflowBuildArtifacts(options);
|
|
111
305
|
await mkdir(stylesDir, { recursive: true });
|
|
112
306
|
for (const [mapName, style] of Object.entries(artifacts.styles)) {
|
|
113
307
|
await writeFile(
|
|
114
|
-
|
|
308
|
+
resolve2(stylesDir, `${mapName}.json`),
|
|
115
309
|
`${JSON.stringify(style, null, 2)}
|
|
116
310
|
`,
|
|
117
311
|
"utf8"
|
|
118
312
|
);
|
|
119
313
|
}
|
|
314
|
+
for (const asset of artifacts.assets) {
|
|
315
|
+
const outputPath = resolve2(outDir, asset.fileName);
|
|
316
|
+
await mkdir(dirname(outputPath), { recursive: true });
|
|
317
|
+
await writeFile(outputPath, asset.source);
|
|
318
|
+
}
|
|
120
319
|
await writeFile(
|
|
121
|
-
|
|
320
|
+
resolve2(outDir, "manifest.json"),
|
|
122
321
|
`${JSON.stringify(artifacts.manifest, null, 2)}
|
|
123
322
|
`,
|
|
124
323
|
"utf8"
|
|
@@ -136,6 +335,16 @@ function createTileflowDevRequestHandler(options = {}) {
|
|
|
136
335
|
}
|
|
137
336
|
const url = new URL(request.url);
|
|
138
337
|
const path = stripTileflowBasePath(url.pathname, basePath);
|
|
338
|
+
const loadPreparedProject = async () => {
|
|
339
|
+
const project = await loadValidTileflowConfig(configPath, {
|
|
340
|
+
cwd,
|
|
341
|
+
fresh: true
|
|
342
|
+
});
|
|
343
|
+
return prepareTileflowProjectIcons(project, {
|
|
344
|
+
assetBaseUrl: `${url.origin}${basePath}`,
|
|
345
|
+
cwd
|
|
346
|
+
});
|
|
347
|
+
};
|
|
139
348
|
if (path === null) {
|
|
140
349
|
return jsonResponse({ error: "Not found" }, 404);
|
|
141
350
|
}
|
|
@@ -145,9 +354,7 @@ function createTileflowDevRequestHandler(options = {}) {
|
|
|
145
354
|
cwd,
|
|
146
355
|
fresh: true
|
|
147
356
|
});
|
|
148
|
-
return htmlResponse(
|
|
149
|
-
previewHtml(getFirstTileflowMapName(project), basePath)
|
|
150
|
-
);
|
|
357
|
+
return htmlResponse(previewHtml(getFirstTileflowMapName(project), basePath));
|
|
151
358
|
}
|
|
152
359
|
if (path === "/manifest.json") {
|
|
153
360
|
const project = await loadValidTileflowConfig(configPath, {
|
|
@@ -163,31 +370,34 @@ function createTileflowDevRequestHandler(options = {}) {
|
|
|
163
370
|
);
|
|
164
371
|
}
|
|
165
372
|
if (path === "/style.json") {
|
|
166
|
-
const
|
|
167
|
-
cwd,
|
|
168
|
-
fresh: true
|
|
169
|
-
});
|
|
373
|
+
const prepared = await loadPreparedProject();
|
|
170
374
|
return jsonResponse(
|
|
171
|
-
createStyleFromProject(project, getFirstTileflowMapName(project), {
|
|
375
|
+
createStyleFromProject(prepared.project, getFirstTileflowMapName(prepared.project), {
|
|
172
376
|
tileBaseUrl
|
|
173
377
|
})
|
|
174
378
|
);
|
|
175
379
|
}
|
|
176
380
|
if (path.startsWith("/styles/")) {
|
|
177
|
-
const
|
|
178
|
-
cwd,
|
|
179
|
-
fresh: true
|
|
180
|
-
});
|
|
381
|
+
const prepared = await loadPreparedProject();
|
|
181
382
|
const mapName = getTileflowStyleMapName(path);
|
|
182
|
-
if (!project.maps[mapName]) {
|
|
383
|
+
if (!prepared.project.maps[mapName]) {
|
|
183
384
|
return jsonResponse({ error: `Unknown map: ${mapName}` }, 404);
|
|
184
385
|
}
|
|
185
386
|
return jsonResponse(
|
|
186
|
-
createStyleFromProject(project, mapName, {
|
|
387
|
+
createStyleFromProject(prepared.project, mapName, {
|
|
187
388
|
tileBaseUrl
|
|
188
389
|
})
|
|
189
390
|
);
|
|
190
391
|
}
|
|
392
|
+
if (path.startsWith("/icons/")) {
|
|
393
|
+
const prepared = await loadPreparedProject();
|
|
394
|
+
const assetName = path.replace(/^\/+/, "");
|
|
395
|
+
const asset = prepared.assets.find((candidate) => candidate.fileName === assetName);
|
|
396
|
+
if (!asset) {
|
|
397
|
+
return jsonResponse({ error: `Unknown icon asset: ${assetName}` }, 404);
|
|
398
|
+
}
|
|
399
|
+
return assetResponse(asset);
|
|
400
|
+
}
|
|
191
401
|
return jsonResponse({ error: "Not found" }, 404);
|
|
192
402
|
} catch (error) {
|
|
193
403
|
options.onError?.(error);
|
|
@@ -245,6 +455,28 @@ function htmlResponse(body, status = 200) {
|
|
|
245
455
|
status
|
|
246
456
|
});
|
|
247
457
|
}
|
|
458
|
+
function assetResponse(asset) {
|
|
459
|
+
const body = typeof asset.source === "string" ? asset.source : Buffer.from(asset.source);
|
|
460
|
+
return new Response(body, {
|
|
461
|
+
headers: {
|
|
462
|
+
"Access-Control-Allow-Origin": "*",
|
|
463
|
+
"Cache-Control": "no-store",
|
|
464
|
+
"Content-Type": asset.contentType
|
|
465
|
+
}
|
|
466
|
+
});
|
|
467
|
+
}
|
|
468
|
+
function resolveAssetBaseUrl(options) {
|
|
469
|
+
if (options.assetBaseUrl !== void 0) {
|
|
470
|
+
return options.assetBaseUrl;
|
|
471
|
+
}
|
|
472
|
+
if (options.styleBaseUrl && !isRelativePublicUrl(options.styleBaseUrl)) {
|
|
473
|
+
return options.styleBaseUrl;
|
|
474
|
+
}
|
|
475
|
+
return "..";
|
|
476
|
+
}
|
|
477
|
+
function isRelativePublicUrl(value) {
|
|
478
|
+
return !(value.startsWith("http://") || value.startsWith("https://") || value.startsWith("/") || value.startsWith("data:"));
|
|
479
|
+
}
|
|
248
480
|
function previewHtml(mapName, basePath) {
|
|
249
481
|
const styleUrl = `${basePath}/styles/${mapName}.json`;
|
|
250
482
|
return `<!doctype html>
|
|
@@ -307,6 +539,7 @@ export {
|
|
|
307
539
|
getFirstTileflowMapName,
|
|
308
540
|
getTileflowMapNames,
|
|
309
541
|
getTileflowStyleMapName,
|
|
542
|
+
getTileflowWatchPaths,
|
|
310
543
|
loadTileflowConfig,
|
|
311
544
|
loadValidTileflowConfig,
|
|
312
545
|
normalizeTileflowBasePath,
|
package/package.json
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tileflow/dev",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.8",
|
|
4
4
|
"description": "Node development utilities for Tileflow configs and builds.",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
6
7
|
"homepage": "https://tileflow.dev",
|
|
7
8
|
"repository": {
|
|
8
9
|
"type": "git",
|
|
@@ -36,7 +37,8 @@
|
|
|
36
37
|
},
|
|
37
38
|
"dependencies": {
|
|
38
39
|
"jiti": "^2.7.0",
|
|
39
|
-
"
|
|
40
|
+
"sharp": "^0.34.5",
|
|
41
|
+
"@tileflow/core": "0.1.0-alpha.8"
|
|
40
42
|
},
|
|
41
43
|
"scripts": {
|
|
42
44
|
"build": "tsup src/index.ts --format esm --dts --clean --platform node",
|