create-fvtt-module 0.0.1

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.
@@ -0,0 +1,36 @@
1
+ {
2
+ "id": "module-template",
3
+ "title": "Module Template",
4
+ "description": "",
5
+ "authors": [
6
+ {
7
+ "name": "Vauxs",
8
+ "discord": "vauxs",
9
+ "twitter": "@ThatVauxs",
10
+ "email": "mrvauxs@gmail.com",
11
+ "ko-fi": "MrVauxs",
12
+ "patreon": "MrVauxs"
13
+ }
14
+ ],
15
+ "version": "dev",
16
+ "compatibility": {
17
+ "minimum": "13",
18
+ "verified": "13"
19
+ },
20
+ "esmodules": [],
21
+ "styles": [],
22
+ "relationships": {},
23
+ "packs": [],
24
+ "packFolders": [],
25
+ "flags": {
26
+ "canUpload": true,
27
+ "hotReload": {
28
+ "extensions": ["css", "json"],
29
+ "paths": ["styles"]
30
+ }
31
+ },
32
+ "url": "",
33
+ "bugs": "",
34
+ "manifest": "",
35
+ "download": ""
36
+ }
@@ -0,0 +1,25 @@
1
+ {
2
+ "name": "new-module",
3
+ "type": "module",
4
+ "private": true,
5
+ "license": "MIT",
6
+ "scripts": {
7
+ "build": "vite build",
8
+ "dev": "vite",
9
+ "extract": "node scripts/extractPacks.mjs",
10
+ "symlink": "node scripts/symlink.mjs"
11
+ },
12
+ "devDependencies": {
13
+ "@foundryvtt/foundryvtt-cli": "^3.0.2",
14
+ "foundryvtt-sync": "https://github.com/MrVauxs/FoundryVTT-Sync",
15
+ "postcss-preset-env": "^10.6.0",
16
+ "@clack/prompts": "^1.0.0-alpha.9",
17
+ "kolorist": "^1.8.0",
18
+ "vite": "^7.3.0",
19
+ "dotenv": "^17.2.3"
20
+ },
21
+ "browserslist": [
22
+ ">5%",
23
+ "not IE 11"
24
+ ]
25
+ }
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { existsSync } from "node:fs";
4
+ import fs from "node:fs/promises";
5
+ import path from "node:path";
6
+ import * as p from "@clack/prompts";
7
+ import { extractPack } from "@foundryvtt/foundryvtt-cli";
8
+ import { yellow } from "kolorist";
9
+ // import moduleJSON from "../module.json" with { type: "json" };
10
+
11
+ const foundryDataDir = "packs/";
12
+ const jsonDataDir = "data/";
13
+
14
+ p.intro(`Extracting ${foundryDataDir} into ${jsonDataDir}...`)
15
+
16
+ const outDir = path.resolve(process.cwd());
17
+ const packsCompiled = path.resolve(outDir, foundryDataDir);
18
+ if (!existsSync(packsCompiled)) {
19
+ p.log.warn("Packs directory does not exist in the build!")
20
+ }
21
+
22
+ const packFolders = await fs.readdir(packsCompiled);
23
+
24
+ if (packFolders.length === 0) p.log.info("No packs to extract!")
25
+
26
+ await p.tasks(
27
+ packFolders.map(pack => ({
28
+ title: `Extracting ${pack}...`,
29
+ task: async () => {
30
+ if (!existsSync(`${jsonDataDir}/${pack}`)) {
31
+ await fs.mkdir(`${jsonDataDir}/${pack}`);
32
+ }
33
+ await extractPack(
34
+ path.resolve(packsCompiled, pack),
35
+ `${jsonDataDir}/${pack}`,
36
+ {
37
+ expandAdventures: true,
38
+ omitVolatile: true,
39
+ folders: false,
40
+ clean: true,
41
+ log: false
42
+ },
43
+ );
44
+ return `Extracted ${yellow(pack)}!`
45
+ }
46
+ })),
47
+ {}
48
+ );
49
+
50
+ p.outro("Finished!");
@@ -0,0 +1,14 @@
1
+ const mod = (await Bun.file("../module.json").json());
2
+ const pack = (await Bun.file("../package.json").json());
3
+
4
+ // Module
5
+ mod.esmodules = [`dist/${mod.id}.js`];
6
+ mod.styles = [`dist/${mod.id}.css`];
7
+
8
+ // Package
9
+ pack.name = mod.id;
10
+
11
+ await Bun.write("../module.json", JSON.stringify(mod, null, "\t"));
12
+ await Bun.write("../package.json", JSON.stringify(pack, null, "\t"));
13
+
14
+ export { };
@@ -0,0 +1,79 @@
1
+ import { lstatSync, rmSync, symlinkSync, unlinkSync, readFileSync, writeFileSync } from "node:fs";
2
+ import { resolve } from "node:path";
3
+ import { homedir } from "node:os";
4
+ import process from "node:process";
5
+ import * as p from "@clack/prompts";
6
+ import moduleJSON from "../module.json" with { type: "json" };
7
+ import { yellow } from "kolorist";
8
+
9
+ p.intro(`${moduleJSON.id} symlink script`)
10
+
11
+ // Store config in user's home directory
12
+ const configPath = resolve(homedir(), ".foundry-symlink-config.json");
13
+
14
+ // Load last known path from config
15
+ let lastPath = null;
16
+ try {
17
+ const config = JSON.parse(readFileSync(configPath, "utf-8"));
18
+ lastPath = config.dataPath;
19
+ } catch {
20
+ // Config doesn't exist yet, that's fine
21
+ }
22
+
23
+ const windowsInstructions = process.platform === "win32" ? " Start with a drive letter (\"C:\\\")." : "";
24
+ const lastFolder = lastPath ? `(last: ${lastPath})` : "";
25
+ const promptPath = await p.text({
26
+ message: `Enter the full path to your Foundry data folder.${windowsInstructions}`,
27
+ placeholder: lastPath,
28
+ initialValue: lastPath,
29
+ validate(val) {
30
+ const value = val.replace(/\W*$/, "").trim();
31
+ if (!value || !/\bData$/.test(value)) {
32
+ return (`"${value}" does not contain ${yellow('/Data')}`);
33
+ }
34
+ }
35
+ });
36
+
37
+ let dataPath = promptPath.replace(/\W*$/, "").trim();
38
+
39
+ if (dataPath !== lastPath) {
40
+ // Ask if user wants to save the path
41
+ const shouldSave = await p.confirm({
42
+ initialValue: true,
43
+ message: `Save "${dataPath}" for future use?`,
44
+ });
45
+
46
+ if (shouldSave) {
47
+ writeFileSync(configPath, JSON.stringify({ dataPath }, null, 2));
48
+ }
49
+ }
50
+
51
+ const symlinkPath = resolve(dataPath, "modules", moduleJSON.id);
52
+ const symlinkStats = lstatSync(symlinkPath, { throwIfNoEntry: false });
53
+ if (symlinkStats) {
54
+ const atPath = symlinkStats.isDirectory() ? "folder" : symlinkStats.isSymbolicLink() ? "symlink" : "file";
55
+ const proceed = await p.confirm({
56
+ initialValue: false,
57
+ message: `A "${moduleJSON.id}" ${atPath} already exists in the "modules" subfolder. Replace with new symlink?`,
58
+ });
59
+ if (!proceed) {
60
+ p.cancel("Aborting.");
61
+ process.exit(0);
62
+ }
63
+ }
64
+
65
+ try {
66
+ if (symlinkStats?.isDirectory()) {
67
+ rmSync(symlinkPath, { recursive: true, force: true });
68
+ } else if (symlinkStats) {
69
+ unlinkSync(symlinkPath);
70
+ }
71
+ symlinkSync(resolve(process.cwd()), symlinkPath);
72
+ } catch (error) {
73
+ if (error instanceof Error) {
74
+ console.error(`An error was encountered trying to create a symlink: ${error.message}`);
75
+ process.exit(1);
76
+ }
77
+ }
78
+
79
+ p.outro(`Symlink successfully created at "${symlinkPath}"!`);
@@ -0,0 +1 @@
1
+ import "./module.css";
File without changes
@@ -0,0 +1,100 @@
1
+ import type { Plugin, UserConfig } from "vite";
2
+ import path from "node:path";
3
+ import fs from 'node:fs';
4
+ import vttSync from "foundryvtt-sync";
5
+ import { defineConfig } from "vite";
6
+ import moduleJSON from "./module.json" with { type: "json" };
7
+ import postcssPresetEnv from "postcss-preset-env";
8
+ import 'dotenv/config'
9
+
10
+ const target = "es2022"; // Build target for the vinal bundle.
11
+ const foundryPort = Number(process.env.FOUNDRY_PORT || 30000); // Which port your FoundryVTT instance is hosted at.
12
+ const devPort = Number(process.env.DEV_PORT || 30001); // Which port you want to use for development.
13
+ const libEntry = "index.ts"; // The main entry file to begin crawling from (root being `src/`).
14
+
15
+ const postcss = {
16
+ inject: false,
17
+ sourceMap: true,
18
+ extensions: [".css"],
19
+ plugins: [postcssPresetEnv],
20
+ };
21
+
22
+ const PACKAGE_ID = `modules/${moduleJSON.id}`;
23
+
24
+ console.log(`Running foundry port ${foundryPort} -> dev port ${devPort}`)
25
+
26
+ export default defineConfig(({ mode: _mode }) => {
27
+ return {
28
+ root: "src/", // Source location / esbuild root.
29
+ base: `/${PACKAGE_ID}/dist`, // Base module path.
30
+ publicDir: false, // No public resources to copy.
31
+ cacheDir: "../.vite-cache", // Relative from root directory.
32
+
33
+ resolve: {
34
+ conditions: ["browser", "import", "default"], // Only use browser-compatible exports from node modules.
35
+ alias: {
36
+ $lib: path.resolve(__dirname, "./src/lib"),
37
+ moduleJSON: path.resolve(__dirname, "./module.json"),
38
+ },
39
+ },
40
+
41
+ esbuild: { target }, // https://esbuild.github.io/api/#transform
42
+
43
+ css: { postcss }, // https://vite.dev/config/shared-options#css-postcss
44
+
45
+ server: {
46
+ port: devPort,
47
+ open: "/game",
48
+ proxy: {
49
+ // Serves static files from main Foundry server.
50
+ [`^(/${PACKAGE_ID}/(assets|lang|packs))`]: `http://localhost:${foundryPort}`,
51
+
52
+ // All other paths besides package ID path are served from main Foundry server.
53
+ [`^(?!/${PACKAGE_ID}/)`]: `http://localhost:${foundryPort}`,
54
+
55
+ // Rewrite incoming `module-id.js` request from Foundry to the dev server libEntry.
56
+ [`/${PACKAGE_ID}/dist/${moduleJSON.id}.js`]: {
57
+ target: `http://localhost:${devPort}/${PACKAGE_ID}/dist`,
58
+ rewrite: () => libEntry,
59
+ },
60
+
61
+ // Enable socket.io from main Foundry server.
62
+ "/socket.io": { target: `ws://localhost:${foundryPort}`, ws: true },
63
+ },
64
+ },
65
+ build: {
66
+ outDir: "../dist", // The output directory.
67
+ emptyOutDir: false,
68
+ sourcemap: true, // Provide a publicly available sourcemap for debuggin purposes.
69
+ target,
70
+ lib: {
71
+ entry: "./" + libEntry,
72
+ formats: ["es"],
73
+ fileName: moduleJSON.id,
74
+ },
75
+ rollupOptions: {
76
+ output: {
77
+ // Rewrite the default style.css to a more recognizable file name.
78
+ assetFileNames: assetInfo =>
79
+ assetInfo.name === "style.css" ? `${moduleJSON.id}.css` : assetInfo.name as string,
80
+ },
81
+ },
82
+ },
83
+
84
+ plugins: [
85
+ vttSync(moduleJSON, {}) as Plugin[], // Build the database from JSON files on build
86
+ {
87
+ name: 'create-dist-files', // Create dummy files for Foundry's tests to pass
88
+ apply: 'serve',
89
+ buildStart() {
90
+ if (!fs.existsSync('dist')) fs.mkdirSync('dist');
91
+
92
+ const files = [...moduleJSON.esmodules, ...moduleJSON.styles];
93
+ for (const name of files) {
94
+ fs.writeFileSync(name, '', { flag: 'a' });
95
+ }
96
+ },
97
+ },
98
+ ],
99
+ } satisfies UserConfig;
100
+ });