@seasketch/geoprocessing
Version:
Geoprocessing and reporting framework for SeaSketch 2.0
193 lines (169 loc) • 5.86 kB
text/typescript
import fs from "fs-extra";
import path from "node:path";
import { globby } from "globby";
import {
Sketch,
SketchCollection,
SketchProperties,
} from "../../src/types/sketch.js";
import { isSketchCollection } from "../../src/helpers/sketch.js";
import { GpStoryConfig } from "../../src/storybook/types.js";
import { v4 as uuid } from "uuid";
// Load example sketches
if (!process.env.PROJECT_PATH) {
throw new Error("PROJECT_PATH environment variable not set");
}
const PROJECT_PATH: string = process.env.PROJECT_PATH;
const sketchDir = path.join(PROJECT_PATH, "examples", "sketches");
if (!fs.existsSync(sketchDir)) {
throw new Error(`Example sketch path ${sketchDir} does not exist`);
}
const outputDir = path.join(PROJECT_PATH, "examples", "output");
if (!fs.existsSync(outputDir)) {
console.error(
`Example output path ${outputDir} does not exist. Have you added to examples/sketches and run the test suite?`,
);
process.exit();
}
const storyDir = path.join(PROJECT_PATH, "src");
// delete old story cache directories
const cachePaths = await globby(path.join(storyDir, "**/.story-cache"), {
onlyDirectories: true,
});
for (const cachePath of cachePaths) {
fs.rmSync(cachePath, { recursive: true });
}
// load report story configs
const storyPaths = await globby(
path.join(storyDir, "**/*.example-stories.ts"),
{
onlyFiles: true,
},
);
const storyConfigs: GpStoryConfig[] = [];
for (const storyPath of storyPaths) {
try {
const { storyConfig } = await import(storyPath);
storyConfigs.push({
...storyConfig,
path: storyPath,
});
} catch {
console.log(`Trouble parsing example ${storyPath}`);
}
}
// load project sketches, that are not from templates (sketch filename is prefixed with gp)
const sketchFilenames = fs
.readdirSync(sketchDir)
.filter((sketchFilename) => path.extname(sketchFilename) === ".json")
.filter(
(sketchFilename) =>
path.basename(sketchFilename).startsWith("gp", 0) === false,
);
const sketches: (Sketch | SketchCollection)[] = [];
for (const sketchFilename of sketchFilenames) {
try {
const sketch: Sketch | SketchCollection = fs.readJSONSync(
path.join(sketchDir, sketchFilename),
) as Sketch;
if (sketch && sketch.properties.name) {
sketches.push(sketch);
}
} catch {
console.log(`Trouble parsing example ${sketchFilename}`);
}
}
// Load related example outputs
const outputs: { sketchName: string; results: any; functionName: string }[] =
[];
for (const sketch of sketches) {
const outputPath = path.join(outputDir, sketch.properties.name);
if (fs.existsSync(outputPath)) {
const outputFilenames = fs.readdirSync(outputPath);
for (const outputFilename of outputFilenames) {
if (path.extname(outputFilename) === ".json") {
outputs.push({
sketchName: sketch.properties.name,
results: fs.readJSONSync(path.join(outputPath, outputFilename)),
functionName: path.basename(outputFilename, ".json"),
});
}
}
}
}
// Generate stories
for (const storyConfig of storyConfigs) {
for (const sketch of sketches) {
console.log("generating story for sketch", sketch.properties.name);
// Pull out relative path from import string and replace with path that will work from story cache subdirectory
if (!fs.existsSync(storyConfig.path!)) {
console.log(
`Story config path ${storyConfig.path} does not exist, skipping`,
);
continue;
}
const importFromCacheStr = `../${storyConfig.componentPath}`
.replace(".ts", ".js")
.replace(".tsx", ".js");
const storyTitleSplit = storyConfig.title.split("/");
// extract story name from title
const storyName = storyTitleSplit.at(-1);
const storyOutDir = path.join(
path.dirname(storyConfig.path!),
".story-cache",
);
const storyOutPath = path.join(
storyOutDir,
`${storyName}-${sketch.properties.name}.stories.tsx`,
);
const exampleOutputs = outputs.filter((output) => {
return output.sketchName === sketch.properties.name;
});
if (!exampleOutputs) {
console.log(
`No results found for sketch ${sketch.properties.name}, skipping`,
);
continue;
}
const childProperties: SketchProperties["childProperties"] = (() => {
if (isSketchCollection(sketch)) {
return sketch.features.map((feature) => feature.properties);
}
return;
})();
const newSketchProperties: SketchProperties = {
...sketch.properties,
...(childProperties ? { childProperties } : {}),
};
const story = `
import React from "react";
import { ${storyConfig.componentName} } from '${importFromCacheStr}';
import {
createReportDecorator,
sampleSketchReportContextValue,
} from "@seasketch/geoprocessing/client-ui";
import Translator from "${path.join(PROJECT_PATH, "src", "components", "TranslatorAsync.js")}";
const contextValue = sampleSketchReportContextValue({
exampleOutputs: ${JSON.stringify(exampleOutputs, null, 2)},
sketchProperties: ${JSON.stringify(newSketchProperties, null, 2)},
projectUrl: "https://example.com/project",
geometryUri: 'https://localhost/${uuid()}',
visibleLayers: [],
language: "en"
});
export const ${sketch.properties.name.replaceAll("-", "_")} = () => (
<Translator>
<${storyConfig.componentName} />
</Translator>
);
export default {
component: ${storyConfig.componentName},
title: '${storyConfig.title}',
name: '${sketch.properties.name}',
decorators: [createReportDecorator(contextValue)],
};
`;
fs.ensureDirSync(storyOutDir);
fs.writeFileSync(storyOutPath, story);
}
}