create-krispya 0.1.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/LICENSE +15 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +497 -0
- package/dist/constants.d.ts +33 -0
- package/dist/constants.js +110 -0
- package/dist/index.d.ts +86 -0
- package/dist/index.js +394 -0
- package/dist/integrations/biome.d.ts +8 -0
- package/dist/integrations/biome.js +80 -0
- package/dist/integrations/drei.d.ts +3 -0
- package/dist/integrations/drei.js +9 -0
- package/dist/integrations/eslint.d.ts +3 -0
- package/dist/integrations/eslint.js +78 -0
- package/dist/integrations/fiber.d.ts +8 -0
- package/dist/integrations/fiber.js +35 -0
- package/dist/integrations/github-pages.d.ts +3 -0
- package/dist/integrations/github-pages.js +58 -0
- package/dist/integrations/handle.d.ts +3 -0
- package/dist/integrations/handle.js +7 -0
- package/dist/integrations/koota.d.ts +8 -0
- package/dist/integrations/koota.js +7 -0
- package/dist/integrations/leva.d.ts +3 -0
- package/dist/integrations/leva.js +7 -0
- package/dist/integrations/offscreen.d.ts +3 -0
- package/dist/integrations/offscreen.js +12 -0
- package/dist/integrations/oxfmt.d.ts +3 -0
- package/dist/integrations/oxfmt.js +27 -0
- package/dist/integrations/oxlint.d.ts +3 -0
- package/dist/integrations/oxlint.js +52 -0
- package/dist/integrations/postprocessing.d.ts +3 -0
- package/dist/integrations/postprocessing.js +12 -0
- package/dist/integrations/prettier.d.ts +3 -0
- package/dist/integrations/prettier.js +28 -0
- package/dist/integrations/rapier.d.ts +3 -0
- package/dist/integrations/rapier.js +7 -0
- package/dist/integrations/triplex.d.ts +26 -0
- package/dist/integrations/triplex.js +159 -0
- package/dist/integrations/uikit.d.ts +3 -0
- package/dist/integrations/uikit.js +7 -0
- package/dist/integrations/viverse.d.ts +3 -0
- package/dist/integrations/viverse.js +74 -0
- package/dist/integrations/xr.d.ts +5 -0
- package/dist/integrations/xr.js +51 -0
- package/dist/integrations/zustand.d.ts +8 -0
- package/dist/integrations/zustand.js +7 -0
- package/dist/lib/array.d.ts +1 -0
- package/dist/lib/array.js +9 -0
- package/dist/merge.d.ts +1 -0
- package/dist/merge.js +26 -0
- package/dist/utils.d.ts +22 -0
- package/dist/utils.js +123 -0
- package/package.json +38 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export function generateFiber(generator, _options) {
|
|
2
|
+
generator.inject("import", `import { Box } from "./box.js"`);
|
|
3
|
+
generator.inject("scene", [
|
|
4
|
+
`<ambientLight intensity={Math.PI / 2} />`,
|
|
5
|
+
`<spotLight position={[10, 10, 10]} angle={0.15} penumbra={1} decay={0} intensity={Math.PI} />`,
|
|
6
|
+
`<pointLight position={[-10, -10, -10]} decay={0} intensity={Math.PI} />`,
|
|
7
|
+
`<Box position={[-1.2, 0, 0]} />`,
|
|
8
|
+
`<Box position={[1.2, 0, 0]} />`,
|
|
9
|
+
].join("\n"));
|
|
10
|
+
generator.addFile("src/box.tsx", {
|
|
11
|
+
type: "text",
|
|
12
|
+
content: `import type { Mesh } from 'three'
|
|
13
|
+
import { useRef, useState } from 'react'
|
|
14
|
+
import { useFrame, ThreeElements } from '@react-three/fiber'
|
|
15
|
+
|
|
16
|
+
export function Box(props: ThreeElements['mesh']) {
|
|
17
|
+
const meshRef = useRef<Mesh>(null!)
|
|
18
|
+
const [hovered, setHover] = useState(false)
|
|
19
|
+
const [active, setActive] = useState(false)
|
|
20
|
+
useFrame((state, delta) => (meshRef.current.rotation.x += delta))
|
|
21
|
+
return (
|
|
22
|
+
<mesh
|
|
23
|
+
{...props}
|
|
24
|
+
ref={meshRef}
|
|
25
|
+
scale={active ? 1.5 : 1}
|
|
26
|
+
onClick={(event) => setActive(!active)}
|
|
27
|
+
onPointerOver={(event) => setHover(true)}
|
|
28
|
+
onPointerOut={(event) => setHover(false)}>
|
|
29
|
+
<boxGeometry args={[1, 1, 1]} />
|
|
30
|
+
<meshStandardMaterial color={hovered ? 'hotpink' : '#2f74c0'} />
|
|
31
|
+
</mesh>
|
|
32
|
+
)
|
|
33
|
+
}`,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
export function generateGithubPages(generator, options) {
|
|
2
|
+
if (options === false ||
|
|
3
|
+
(generator.options.packageManager ?? "npm") != "npm") {
|
|
4
|
+
return;
|
|
5
|
+
}
|
|
6
|
+
generator.addFile(".github/workflows/gh-pages.yml", {
|
|
7
|
+
type: "text",
|
|
8
|
+
content: `name: Deploy to Github Pages
|
|
9
|
+
|
|
10
|
+
on:
|
|
11
|
+
push:
|
|
12
|
+
branches:
|
|
13
|
+
- main
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
build-and-deploy:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
permissions:
|
|
19
|
+
pages: write
|
|
20
|
+
contents: read
|
|
21
|
+
id-token: write
|
|
22
|
+
|
|
23
|
+
environment:
|
|
24
|
+
name: github-pages
|
|
25
|
+
url: \${{ steps.deployment.outputs.page_url }}
|
|
26
|
+
|
|
27
|
+
steps:
|
|
28
|
+
- name: Checkout code
|
|
29
|
+
uses: actions/checkout@v3
|
|
30
|
+
|
|
31
|
+
- name: Setup Node
|
|
32
|
+
uses: actions/setup-node@v3
|
|
33
|
+
with:
|
|
34
|
+
node-version: 20
|
|
35
|
+
|
|
36
|
+
- name: Install dependencies
|
|
37
|
+
run: npm install
|
|
38
|
+
|
|
39
|
+
- name: Build project
|
|
40
|
+
run: npm run build
|
|
41
|
+
|
|
42
|
+
- name: Upload artifact
|
|
43
|
+
uses: actions/upload-pages-artifact@v3
|
|
44
|
+
with:
|
|
45
|
+
path: './dist'
|
|
46
|
+
|
|
47
|
+
- name: Deploy to GitHub Pages
|
|
48
|
+
id: deployment
|
|
49
|
+
uses: actions/deploy-pages@v4
|
|
50
|
+
`,
|
|
51
|
+
});
|
|
52
|
+
generator.inject("readme-start", `A github pages deployment action is configurd.`);
|
|
53
|
+
if (generator.options.githubUserName != null &&
|
|
54
|
+
generator.options.githubRepoName != null) {
|
|
55
|
+
const address = `${generator.options.githubUserName}.github.io/${generator.options.githubRepoName}`;
|
|
56
|
+
generator.inject("readme-start", `Your app will be publish at [${address}](https://${address}) once the github action is finished.\n`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export function generateHandle(generator, options) {
|
|
2
|
+
if (options == null) {
|
|
3
|
+
return;
|
|
4
|
+
}
|
|
5
|
+
generator.addDependency("@react-three/handle", "^6.6.16");
|
|
6
|
+
generator.inject("readme-libraries", `[@react-three/handle](https://pmndrs.github.io/xr/docs/handles/introduction) - interactive controls and handles for your 3D objects`);
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export function generateKoota(generator, options) {
|
|
2
|
+
if (options == null) {
|
|
3
|
+
return;
|
|
4
|
+
}
|
|
5
|
+
generator.addDependency("koota", "^0.4.0");
|
|
6
|
+
generator.inject("readme-libraries", `[koota](https://github.com/pmndrs/koota) - ECS-based state management library optimized for real-time apps, games, and XR experiences`);
|
|
7
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export function generateLeva(generator, options) {
|
|
2
|
+
if (options == null) {
|
|
3
|
+
return;
|
|
4
|
+
}
|
|
5
|
+
generator.addDependency("leva", "^0.10.0");
|
|
6
|
+
generator.inject("readme-libraries", `[leva](https://github.com/pmndrs/leva) - HTML GUI panel for React with lightweight, beautiful and extensible controls`);
|
|
7
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
export function generateOffscreen(generator, options) {
|
|
3
|
+
if (options == null) {
|
|
4
|
+
return;
|
|
5
|
+
}
|
|
6
|
+
if (generator.options.xr != null) {
|
|
7
|
+
console.info(chalk.blue("Info:"), "@react-three/offscreen is disabled because it is not supported with XR");
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
generator.addDependency("@react-three/offscreen", "^0.0.8");
|
|
11
|
+
generator.inject("readme-libraries", `[@react-three/offscreen](https://github.com/pmndrs/offscreen) - Offload your scene to a worker thread for better performance`);
|
|
12
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { defaultFormatterConfig } from "../constants.js";
|
|
2
|
+
export function generateOxfmt(generator, options) {
|
|
3
|
+
if (options == null) {
|
|
4
|
+
return;
|
|
5
|
+
}
|
|
6
|
+
const version = generator.versions.oxfmt ?? "0.1.0";
|
|
7
|
+
generator.addDependency("oxfmt", `^${version}`);
|
|
8
|
+
// Add oxfmt config using common formatter settings (Prettier-compatible format)
|
|
9
|
+
const oxfmtConfig = {
|
|
10
|
+
printWidth: defaultFormatterConfig.printWidth,
|
|
11
|
+
tabWidth: defaultFormatterConfig.tabWidth,
|
|
12
|
+
useTabs: defaultFormatterConfig.useTabs,
|
|
13
|
+
semi: defaultFormatterConfig.semi,
|
|
14
|
+
singleQuote: defaultFormatterConfig.singleQuote,
|
|
15
|
+
trailingComma: defaultFormatterConfig.trailingComma,
|
|
16
|
+
bracketSpacing: defaultFormatterConfig.bracketSpacing,
|
|
17
|
+
arrowParens: defaultFormatterConfig.arrowParens,
|
|
18
|
+
};
|
|
19
|
+
generator.addFile(".oxfmt.json", {
|
|
20
|
+
type: "text",
|
|
21
|
+
content: JSON.stringify(oxfmtConfig, null, 2),
|
|
22
|
+
});
|
|
23
|
+
generator.addScript("format", "oxfmt --write .");
|
|
24
|
+
generator.inject("readme-tools", "[Oxfmt](https://oxc.rs/docs/guide/usage/formatter) - Fast Prettier-compatible code formatter");
|
|
25
|
+
generator.inject("vscode-extension-suggestion", "oxc.oxc-vscode");
|
|
26
|
+
generator.addVscodeSetting("editor.defaultFormatter", "oxc.oxc-vscode");
|
|
27
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { defaultLinterConfig } from "../constants.js";
|
|
2
|
+
import { getBaseTemplate } from "../index.js";
|
|
3
|
+
// Helper to convert level to oxlint format
|
|
4
|
+
function toOxlintLevel(level) {
|
|
5
|
+
return level;
|
|
6
|
+
}
|
|
7
|
+
export function generateOxlint(generator, options) {
|
|
8
|
+
if (options == null) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
const version = generator.versions.oxlint ?? "0.16.0";
|
|
12
|
+
generator.addDependency("oxlint", `^${version}`);
|
|
13
|
+
const { rules } = defaultLinterConfig;
|
|
14
|
+
// Check if it's a React project
|
|
15
|
+
const template = generator.options.template ?? "vanilla";
|
|
16
|
+
const baseTemplate = getBaseTemplate(template);
|
|
17
|
+
const isReact = baseTemplate === "react" || baseTemplate === "r3f";
|
|
18
|
+
// Build plugins list - add react plugin for React projects
|
|
19
|
+
const plugins = ["unicorn", "typescript", "oxc"];
|
|
20
|
+
if (isReact) {
|
|
21
|
+
plugins.push("react");
|
|
22
|
+
}
|
|
23
|
+
// Add oxlint config with plugins and common rules
|
|
24
|
+
const oxlintConfig = {
|
|
25
|
+
$schema: "./node_modules/oxlint/configuration_schema.json",
|
|
26
|
+
plugins,
|
|
27
|
+
rules: {
|
|
28
|
+
"no-unused-vars": [
|
|
29
|
+
toOxlintLevel(rules.noUnusedVars.level),
|
|
30
|
+
{
|
|
31
|
+
argsIgnorePattern: rules.noUnusedVars.argsIgnorePattern,
|
|
32
|
+
varsIgnorePattern: rules.noUnusedVars.varsIgnorePattern,
|
|
33
|
+
caughtErrorsIgnorePattern: rules.noUnusedVars.caughtErrorsIgnorePattern,
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
"no-useless-escape": "off",
|
|
37
|
+
"no-unused-expressions": [
|
|
38
|
+
toOxlintLevel(rules.noUnusedExpressions.level),
|
|
39
|
+
{ allowShortCircuit: rules.noUnusedExpressions.allowShortCircuit },
|
|
40
|
+
],
|
|
41
|
+
},
|
|
42
|
+
ignorePatterns: defaultLinterConfig.ignorePatterns,
|
|
43
|
+
};
|
|
44
|
+
generator.addFile("oxlint.json", {
|
|
45
|
+
type: "text",
|
|
46
|
+
content: JSON.stringify(oxlintConfig, null, 2),
|
|
47
|
+
});
|
|
48
|
+
generator.addScript("lint", "oxlint");
|
|
49
|
+
generator.inject("readme-tools", "[Oxlint](https://oxc.rs/docs/guide/usage/linter) - A fast linter for JavaScript and TypeScript");
|
|
50
|
+
generator.inject("vscode-extension-suggestion", "oxc.oxc-vscode");
|
|
51
|
+
generator.addVscodeSetting("oxc.enable", true);
|
|
52
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import chalk from "chalk";
|
|
2
|
+
export function generatePostprocessing(generator, options) {
|
|
3
|
+
if (options == null) {
|
|
4
|
+
return;
|
|
5
|
+
}
|
|
6
|
+
if (generator.options.xr != null) {
|
|
7
|
+
console.info(chalk.blue("Info:"), "@react-three/postprocessing is disabled because it is not supported with XR");
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
10
|
+
generator.addDependency("@react-three/postprocessing", "^3.0.4");
|
|
11
|
+
generator.inject("readme-libraries", `[@react-three/postprocessing](https://react-postprocessing.docs.pmnd.rs/) - Post-processing effects for @react-three/fiber`);
|
|
12
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { defaultFormatterConfig } from "../constants.js";
|
|
2
|
+
export function generatePrettier(generator, options) {
|
|
3
|
+
if (options == null) {
|
|
4
|
+
return;
|
|
5
|
+
}
|
|
6
|
+
const version = generator.versions.prettier ?? "3.4.2";
|
|
7
|
+
generator.addDependency("prettier", `^${version}`);
|
|
8
|
+
// Add prettier config using common formatter settings
|
|
9
|
+
const prettierConfig = {
|
|
10
|
+
$schema: "https://json.schemastore.org/prettierrc",
|
|
11
|
+
printWidth: defaultFormatterConfig.printWidth,
|
|
12
|
+
tabWidth: defaultFormatterConfig.tabWidth,
|
|
13
|
+
useTabs: defaultFormatterConfig.useTabs,
|
|
14
|
+
semi: defaultFormatterConfig.semi,
|
|
15
|
+
singleQuote: defaultFormatterConfig.singleQuote,
|
|
16
|
+
trailingComma: defaultFormatterConfig.trailingComma,
|
|
17
|
+
bracketSpacing: defaultFormatterConfig.bracketSpacing,
|
|
18
|
+
arrowParens: defaultFormatterConfig.arrowParens,
|
|
19
|
+
};
|
|
20
|
+
generator.addFile(".prettierrc", {
|
|
21
|
+
type: "text",
|
|
22
|
+
content: JSON.stringify(prettierConfig, null, 2),
|
|
23
|
+
});
|
|
24
|
+
generator.addScript("format", "prettier --write .");
|
|
25
|
+
generator.inject("readme-tools", "[Prettier](https://prettier.io/) - Opinionated code formatter");
|
|
26
|
+
generator.inject("vscode-extension-suggestion", "esbenp.prettier-vscode");
|
|
27
|
+
generator.addVscodeSetting("editor.defaultFormatter", "esbenp.prettier-vscode");
|
|
28
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export function generateRapier(generator, options) {
|
|
2
|
+
if (options == null) {
|
|
3
|
+
return;
|
|
4
|
+
}
|
|
5
|
+
generator.addDependency("@react-three/rapier", "^2.1.0");
|
|
6
|
+
generator.inject("readme-libraries", `[@react-three/rapier](https://github.com/pmndrs/react-three-rapier) - Physics based on Rapier for your @react-three/fiber scene`);
|
|
7
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Generator } from "../index.js";
|
|
2
|
+
export type GenerateTriplexOptions = {} | boolean;
|
|
3
|
+
export type PropValue = {
|
|
4
|
+
declaredPropDefaultValue: unknown;
|
|
5
|
+
declaredPropName: string;
|
|
6
|
+
declaredPropType: string;
|
|
7
|
+
propName: string;
|
|
8
|
+
propValue: string;
|
|
9
|
+
};
|
|
10
|
+
export type ProviderDefinition = Record<string, {
|
|
11
|
+
component: string;
|
|
12
|
+
type: "wrapped-jsx";
|
|
13
|
+
import: string;
|
|
14
|
+
props?: PropValue[];
|
|
15
|
+
} | {
|
|
16
|
+
code: string;
|
|
17
|
+
type: "inline-jsx";
|
|
18
|
+
import: string;
|
|
19
|
+
props?: PropValue[];
|
|
20
|
+
} | {
|
|
21
|
+
code: string;
|
|
22
|
+
type: "layout-effect";
|
|
23
|
+
import: string;
|
|
24
|
+
props?: PropValue[];
|
|
25
|
+
}>;
|
|
26
|
+
export declare function generateTriplex(generator: Generator, options: GenerateTriplexOptions | undefined): void;
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { unique } from "../lib/array.js";
|
|
2
|
+
function generateProvidersModule(generator) {
|
|
3
|
+
const canvasProviders = [];
|
|
4
|
+
const globalProviders = [];
|
|
5
|
+
const providerDefs = {
|
|
6
|
+
uikit: {
|
|
7
|
+
type: "layout-effect",
|
|
8
|
+
props: [
|
|
9
|
+
{
|
|
10
|
+
declaredPropDefaultValue: '"light"',
|
|
11
|
+
declaredPropName: "colorMode",
|
|
12
|
+
propName: "colorMode",
|
|
13
|
+
propValue: "colorMode",
|
|
14
|
+
declaredPropType: '"light" | "dark"',
|
|
15
|
+
},
|
|
16
|
+
],
|
|
17
|
+
code: `
|
|
18
|
+
setPreferredColorScheme(colorMode);
|
|
19
|
+
`,
|
|
20
|
+
import: 'import { setPreferredColorScheme } from "@react-three/uikit"',
|
|
21
|
+
},
|
|
22
|
+
rapier: {
|
|
23
|
+
component: "Physics",
|
|
24
|
+
type: "wrapped-jsx",
|
|
25
|
+
import: 'import { Physics } from "@react-three/rapier";',
|
|
26
|
+
props: [
|
|
27
|
+
{
|
|
28
|
+
declaredPropDefaultValue: false,
|
|
29
|
+
declaredPropName: "physicsEnabled",
|
|
30
|
+
propName: "paused",
|
|
31
|
+
propValue: "!physicsEnabled",
|
|
32
|
+
declaredPropType: "boolean",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
declaredPropDefaultValue: true,
|
|
36
|
+
declaredPropName: "debugPhysics",
|
|
37
|
+
propName: "debug",
|
|
38
|
+
propValue: "debugPhysics",
|
|
39
|
+
declaredPropType: "boolean",
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
},
|
|
43
|
+
postprocessing: {
|
|
44
|
+
type: "inline-jsx",
|
|
45
|
+
code: `
|
|
46
|
+
<EffectComposer enabled={postProcessingEnabled}>
|
|
47
|
+
<DepthOfField
|
|
48
|
+
focusDistance={0}
|
|
49
|
+
focalLength={0.02}
|
|
50
|
+
bokehScale={2}
|
|
51
|
+
height={480}
|
|
52
|
+
/>
|
|
53
|
+
<Bloom luminanceThreshold={0} luminanceSmoothing={0.9} height={300} />
|
|
54
|
+
</EffectComposer>
|
|
55
|
+
`,
|
|
56
|
+
import: 'import { Bloom, DepthOfField, EffectComposer } from "@react-three/postprocessing";',
|
|
57
|
+
props: [
|
|
58
|
+
{
|
|
59
|
+
declaredPropDefaultValue: true,
|
|
60
|
+
declaredPropName: "postProcessingEnabled",
|
|
61
|
+
propName: "enabled",
|
|
62
|
+
propValue: "postProcessingEnabled",
|
|
63
|
+
declaredPropType: "boolean",
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
},
|
|
67
|
+
};
|
|
68
|
+
if (generator.options.rapier) {
|
|
69
|
+
canvasProviders.push("rapier");
|
|
70
|
+
}
|
|
71
|
+
if (!!generator.options.postprocessing && !generator.options.xr) {
|
|
72
|
+
canvasProviders.push("postprocessing");
|
|
73
|
+
}
|
|
74
|
+
if (generator.options.uikit) {
|
|
75
|
+
globalProviders.push("uikit");
|
|
76
|
+
}
|
|
77
|
+
function generateProviderFunction(name, { jsdoc, providers }) {
|
|
78
|
+
const resolvedProviders = providers.map((provider) => providerDefs[provider]);
|
|
79
|
+
const providerProps = resolvedProviders.flatMap((provider) => provider.props || []);
|
|
80
|
+
const providerImports = resolvedProviders.flatMap((provider) => provider.import);
|
|
81
|
+
const wrappedComponents = resolvedProviders.filter((provider) => provider.type === "wrapped-jsx");
|
|
82
|
+
const inlineComponents = resolvedProviders.filter((provider) => provider.type === "inline-jsx");
|
|
83
|
+
const layoutEffects = resolvedProviders.filter((provider) => provider.type === "layout-effect");
|
|
84
|
+
const declaredProps = providerProps
|
|
85
|
+
.map((prop) => `${prop.declaredPropName} = ${prop.declaredPropDefaultValue}`)
|
|
86
|
+
.join(", ");
|
|
87
|
+
const declaredTypes = providerProps
|
|
88
|
+
.map((prop) => `${prop.declaredPropName}?: ${prop.declaredPropType}`)
|
|
89
|
+
.join("; ");
|
|
90
|
+
const reactImports = ["type ReactNode"];
|
|
91
|
+
if (layoutEffects.length) {
|
|
92
|
+
reactImports.push("useLayoutEffect");
|
|
93
|
+
}
|
|
94
|
+
return {
|
|
95
|
+
reactImports,
|
|
96
|
+
imports: providerImports,
|
|
97
|
+
code: `
|
|
98
|
+
/**
|
|
99
|
+
${jsdoc
|
|
100
|
+
.split("\n")
|
|
101
|
+
.map((line) => ` * ${line}`)
|
|
102
|
+
.join("\n")}
|
|
103
|
+
*/
|
|
104
|
+
export function ${name}({ children, ${declaredProps} }: { children: ReactNode; ${declaredTypes} }) {
|
|
105
|
+
${layoutEffects.length
|
|
106
|
+
? `
|
|
107
|
+
useLayoutEffect(() => {
|
|
108
|
+
${layoutEffects.map((effect) => effect.code).join("\n")}
|
|
109
|
+
}, [${layoutEffects.map((effect) => effect.props?.[0]?.propValue)}]);
|
|
110
|
+
`
|
|
111
|
+
: ""}
|
|
112
|
+
return (
|
|
113
|
+
<>
|
|
114
|
+
${inlineComponents.map((provider) => provider.code)}
|
|
115
|
+
${wrappedComponents.reduce((acc, provider) => {
|
|
116
|
+
const props = provider.props
|
|
117
|
+
?.map((prop) => `${prop.propName}={${prop.propValue}}`)
|
|
118
|
+
.join(" ");
|
|
119
|
+
return `<${provider.component} ${props}>${acc}</${provider.component}>`;
|
|
120
|
+
}, "{children}")}
|
|
121
|
+
</>
|
|
122
|
+
);
|
|
123
|
+
}`,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
const global = generateProviderFunction("GlobalProvider", {
|
|
127
|
+
jsdoc: "The global provider is rendered at the root of your application,\nuse it to set up global configuration like themes.\nProps defined on this component appear as controls inside Triplex.\n\nSee: https://triplex.dev/docs/building-your-scene/providers#global-provider",
|
|
128
|
+
providers: globalProviders,
|
|
129
|
+
});
|
|
130
|
+
const canvas = generateProviderFunction("CanvasProvider", {
|
|
131
|
+
jsdoc: "The canvas provider is rendered as a child inside the React Three Fiber canvas,\nuse it to set up canvas specific configuration like post-processing and physics.\nProps defined on this component appear as controls inside Triplex.\n\nSee: https://triplex.dev/docs/building-your-scene/providers#canvas-provider",
|
|
132
|
+
providers: canvasProviders,
|
|
133
|
+
});
|
|
134
|
+
return `
|
|
135
|
+
import { ${unique(global.reactImports, canvas.reactImports).sort().join(", ")} } from "react";
|
|
136
|
+
${unique(global.imports, canvas.imports).sort().join("\n")}
|
|
137
|
+
|
|
138
|
+
${global.code}
|
|
139
|
+
${canvas.code}
|
|
140
|
+
`;
|
|
141
|
+
}
|
|
142
|
+
export function generateTriplex(generator, options) {
|
|
143
|
+
if (options == null) {
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
generator.inject("vscode-extension-suggestion", "trytriplex.triplex-vsce");
|
|
147
|
+
generator.inject("readme-tools", `[Triplex](https://triplex.dev) - Your visual workspace for React / Three Fiber. Get started by installing [Triplex for VS Code](https://triplex.dev/docs/get-started/vscode). Don't use Visual Studio Code? Download [Triplex Standalone](https://triplex.dev/docs/get-started/standalone).`);
|
|
148
|
+
generator.addFile(".triplex/providers.tsx", {
|
|
149
|
+
content: generateProvidersModule(generator),
|
|
150
|
+
type: "text",
|
|
151
|
+
});
|
|
152
|
+
generator.addFile(".triplex/config.json", {
|
|
153
|
+
content: JSON.stringify({
|
|
154
|
+
$schema: "https://triplex.dev/config.schema.json",
|
|
155
|
+
provider: "./providers.tsx",
|
|
156
|
+
}, null, 2),
|
|
157
|
+
type: "text",
|
|
158
|
+
});
|
|
159
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export function generateUikit(generator, options) {
|
|
2
|
+
if (options == null) {
|
|
3
|
+
return;
|
|
4
|
+
}
|
|
5
|
+
generator.addDependency("@react-three/uikit", "^0.8.15");
|
|
6
|
+
generator.inject("readme-libraries", `[@react-three/uikit](https://pmndrs.github.io/uikit/docs/) - UI primitives for React Three Fiber`);
|
|
7
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
export function generateViverse(generator, options) {
|
|
2
|
+
if (options == null || (generator.options.packageManager ?? "npm") != "npm") {
|
|
3
|
+
return;
|
|
4
|
+
}
|
|
5
|
+
generator.addFile(".github/workflows/viverse.yml", {
|
|
6
|
+
type: "text",
|
|
7
|
+
content: `name: Deploy to Viverse
|
|
8
|
+
|
|
9
|
+
on:
|
|
10
|
+
push:
|
|
11
|
+
branches:
|
|
12
|
+
- main
|
|
13
|
+
workflow_dispatch:
|
|
14
|
+
|
|
15
|
+
jobs:
|
|
16
|
+
check-secrets:
|
|
17
|
+
runs-on: ubuntu-latest
|
|
18
|
+
outputs:
|
|
19
|
+
secrets-available: \${{ steps.check.outputs.secrets-available }}
|
|
20
|
+
steps:
|
|
21
|
+
- id: check
|
|
22
|
+
run: |
|
|
23
|
+
if [[ -n "\${{ secrets.VIVERSE_EMAIL }}" && -n "\${{ secrets.VIVERSE_PASSWORD }}" ]]; then
|
|
24
|
+
echo "secrets-available=true" >> $GITHUB_OUTPUT
|
|
25
|
+
else
|
|
26
|
+
echo "secrets-available=false" >> $GITHUB_OUTPUT
|
|
27
|
+
fi
|
|
28
|
+
|
|
29
|
+
build-and-deploy:
|
|
30
|
+
runs-on: ubuntu-latest
|
|
31
|
+
needs: check-secrets
|
|
32
|
+
# Only run if secrets are present
|
|
33
|
+
if: needs.check-secrets.outputs.secrets-available == 'true'
|
|
34
|
+
permissions:
|
|
35
|
+
contents: read
|
|
36
|
+
|
|
37
|
+
steps:
|
|
38
|
+
- name: Checkout code
|
|
39
|
+
uses: actions/checkout@v3
|
|
40
|
+
|
|
41
|
+
- name: Setup Node
|
|
42
|
+
uses: actions/setup-node@v3
|
|
43
|
+
with:
|
|
44
|
+
node-version: 22
|
|
45
|
+
|
|
46
|
+
- name: Install dependencies
|
|
47
|
+
run: npm install
|
|
48
|
+
|
|
49
|
+
- name: Build project
|
|
50
|
+
run: npm run build
|
|
51
|
+
|
|
52
|
+
- name: Viverse Login
|
|
53
|
+
run: npx viverse-cli auth login -e \${{ secrets.VIVERSE_EMAIL }} -p \${{ secrets.VIVERSE_PASSWORD }}
|
|
54
|
+
|
|
55
|
+
- name: Deploy to Viverse
|
|
56
|
+
run: npx viverse-cli app publish ./dist --auto-create-app --name ${generator.options.name}
|
|
57
|
+
|
|
58
|
+
`,
|
|
59
|
+
});
|
|
60
|
+
generator.addDependency("@viverse/cli", "^0.9.5-beta.8");
|
|
61
|
+
generator.inject("readme-start", `A GitHub CI/CD workflow for publishing to Viverse is configured.
|
|
62
|
+
|
|
63
|
+
To use publish to viverse via the CI/CD workflow:
|
|
64
|
+
1. Set \`VIVERSE_EMAIL\` and \`VIVERSE_PASSWORD\` secrets in your repository settings under \`Secrets and Variables\` > \`Actions\` > \`New repository secret\`
|
|
65
|
+
2. Manually trigger the "Deploy to Viverse" workflow or push to the main branch
|
|
66
|
+
|
|
67
|
+
**Manual CLI Upload:**
|
|
68
|
+
You can also upload your project manually using the Viverse CLI:
|
|
69
|
+
\`\`\`bash
|
|
70
|
+
viverse-cli auth login -e <email> -p <password>
|
|
71
|
+
npm run build
|
|
72
|
+
viverse-cli app publish ./dist --auto-create-app --name ${generator.options.name}
|
|
73
|
+
\`\`\`\n`);
|
|
74
|
+
}
|