Skip to content

Commit 650eb26

Browse files
committed
feat(core): add experience generation (beta)
1 parent 93d6dc6 commit 650eb26

File tree

8 files changed

+223
-13
lines changed

8 files changed

+223
-13
lines changed

CHANGELOG.md

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,16 @@
1-
2-
31
## [2.0.0-beta.18](https://github.com/angular-threejs/angular-three/compare/2.0.0-beta.17...2.0.0-beta.18) (2023-08-28)
42

5-
63
### Features
74

8-
* **soba:** gizmo ([8ac565c](https://github.com/angular-threejs/angular-three/commit/8ac565c50c8f8d8e3639d09f5a51f76393e5c163))
9-
5+
- **soba:** gizmo ([8ac565c](https://github.com/angular-threejs/angular-three/commit/8ac565c50c8f8d8e3639d09f5a51f76393e5c163))
106

117
### Bug Fixes
128

13-
* **soba:** reverse occlusion logic in HTML ([17c5123](https://github.com/angular-threejs/angular-three/commit/17c5123a0cd6e9362ccb91c7c8646b1f3e481e58))
14-
9+
- **soba:** reverse occlusion logic in HTML ([17c5123](https://github.com/angular-threejs/angular-three/commit/17c5123a0cd6e9362ccb91c7c8646b1f3e481e58))
1510

1611
### Documentations
1712

18-
* more on aviator ([aea07c6](https://github.com/angular-threejs/angular-three/commit/aea07c6b1007887169ae4b645baac57b2a721ea9))
13+
- more on aviator ([aea07c6](https://github.com/angular-threejs/angular-three/commit/aea07c6b1007887169ae4b645baac57b2a721ea9))
1914

2015
## [2.0.0-beta.17](https://github.com/angular-threejs/angular-three/compare/2.0.0-beta.16...2.0.0-beta.17) (2023-08-23)
2116

@@ -79,4 +74,4 @@
7974

8075
### Features
8176

82-
- **core:** generate core ([e1cf6c7](https://github.com/angular-threejs/angular-three/commit/e1cf6c7422668afbbc0f767a444bcc591e1a6903))
77+
- **core:** generate core ([e1cf6c7](https://github.com/angular-threejs/angular-three/commit/e1cf6c7422668afbbc0f767a444bcc591e1a6903))

libs/plugin/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
{
2-
"type": "commonjs"
2+
"type": "commonjs",
3+
"generators": "./generators.json"
34
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
<ngt-mesh (beforeRender)="onBeforeRender($event)">
2+
<ngt-box-geometry />
3+
<ngt-mesh-basic-material color="hotpink" />
4+
</ngt-mesh>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { CUSTOM_ELEMENTS_SCHEMA, Component } from '@angular/core';
2+
import { Mesh, BoxGeometry, MeshBasicMaterial } from 'three';
3+
4+
extend({ Mesh, BoxGeometry, MeshBasicMaterial });
5+
6+
@Component({
7+
standalone: true,
8+
templateUrl: './experience.component.html',
9+
schemas: [CUSTOM_ELEMENTS_SCHEMA]
10+
})
11+
export class Experience {
12+
onBeforeRender({ object, state: { delta }}: NgtBeforeRenderEvent<Mesh>) {
13+
object.rotation.x += delta;
14+
object.rotation.y += delta;
15+
}
16+
}
17+

libs/plugin/src/generators/init/generator.ts

Lines changed: 154 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,31 @@
1-
import { addDependenciesToPackageJson, installPackagesTask, logger, readJson, updateJson, type Tree } from '@nx/devkit';
1+
import {
2+
addDependenciesToPackageJson,
3+
generateFiles,
4+
installPackagesTask,
5+
logger,
6+
readJson,
7+
readProjectConfiguration,
8+
updateJson,
9+
type Tree,
10+
} from '@nx/devkit';
11+
import { tsquery } from '@phenomnomnominal/tsquery';
12+
import { prompt } from 'enquirer';
13+
import { join } from 'node:path';
14+
import {
15+
ArrayLiteralExpression,
16+
ClassDeclaration,
17+
NoSubstitutionTemplateLiteral,
18+
SourceFile,
19+
factory,
20+
} from 'typescript';
221
import { addMetadataJson } from '../utils';
322
import { ANGULAR_THREE_VERSION, THREE_TYPE_VERSION, THREE_VERSION } from '../versions';
423

5-
export default async function (tree: Tree) {
24+
type Schema = {
25+
project?: string;
26+
};
27+
28+
export default async function (tree: Tree, { project }: Schema) {
629
logger.log('Initializing Angular Three...');
730

831
const packageJson = readJson(tree, 'package.json');
@@ -30,6 +53,135 @@ export default async function (tree: Tree) {
3053

3154
addMetadataJson(tree, 'angular-three/metadata.json');
3255

56+
const { generateExperience } = await prompt<{ generateExperience: 'append' | 'replace' | 'none' }>({
57+
type: 'select',
58+
name: 'generateExperience',
59+
message: 'Generate an Experience component?',
60+
choices: [
61+
{ value: 'append', name: 'append', message: 'Append <ngt-canvas /> to AppComponent template' },
62+
{ value: 'replace', name: 'replace', message: 'Replace AppComponent template with <ngt-canvas />' },
63+
{ value: 'none', name: 'none', message: 'Do not generate an Experience component' },
64+
],
65+
initial: 2,
66+
});
67+
68+
if (generateExperience !== 'none') {
69+
const isNx = tree.exists('nx.json');
70+
const rootProjectJson = readJson(tree, 'project.json');
71+
72+
if (!project) {
73+
if (isNx) {
74+
if (!rootProjectJson) {
75+
throw new Error(`
76+
It seems like your workspace is an Integrated workspace but you did not provide a "project" name.
77+
Please retry the generator with a "--project" specified.`);
78+
}
79+
const rootName = rootProjectJson['name'];
80+
if (rootName === packageJson['name']) {
81+
project = rootName;
82+
}
83+
}
84+
}
85+
86+
if (!project) {
87+
throw new Error(`
88+
Angular Three generator could not find a default "project".
89+
Please retry the generator with a "--project" specified.`);
90+
}
91+
const projectConfig = readProjectConfiguration(tree, project);
92+
const sourceRoot = projectConfig.sourceRoot;
93+
94+
if (sourceRoot) {
95+
// generate Experience component
96+
generateFiles(tree, join(__dirname, 'files'), sourceRoot, { __tmpl__: '' });
97+
98+
const { isStandalone } = await prompt<{ isStandalone: boolean }>({
99+
type: 'confirm',
100+
initial: false,
101+
name: 'isStandalone',
102+
message: 'Is your project standalone?',
103+
});
104+
const appComponentPath = `${sourceRoot}/app.component.ts`;
105+
const exist = tree.exists(appComponentPath);
106+
const appComponentTemplatePath = `${sourceRoot}/app.component.html`;
107+
const templateExist = tree.exists(appComponentTemplatePath);
108+
const appComponentContent = exist ? tree.read(appComponentPath, 'utf8') : null;
109+
const appComponentTemplateContent = templateExist ? tree.read(appComponentTemplatePath, 'utf8') : null;
110+
if (isStandalone) {
111+
if (!appComponentContent) {
112+
logger.warn(`
113+
AppComponent not found at ${appComponentPath}. Angular Three was initialized successfully but an Experience component was not generated.`);
114+
} else {
115+
let updatedContent = tsquery.replace(
116+
appComponentContent,
117+
'Identifier[name="imports"] ~ ArrayLiteralExpression',
118+
(node: ArrayLiteralExpression) =>
119+
factory
120+
.updateArrayLiteralExpression(node, [
121+
...node.elements,
122+
factory.createIdentifier('NgtCanvas'),
123+
])
124+
.getFullText(),
125+
);
126+
updatedContent = tsquery.replace(updatedContent, 'SourceFile', (node: SourceFile) => {
127+
return `
128+
import { NgtCanvas } from 'angular-three';
129+
import { Experience } from './experience/experience.component';
130+
${node.getFullText()}`;
131+
});
132+
updatedContent = tsquery.replace(updatedContent, 'ClassDeclaration', (node: ClassDeclaration) =>
133+
factory
134+
.updateClassDeclaration(
135+
node,
136+
node.modifiers,
137+
node.name,
138+
node.typeParameters,
139+
node.heritageClauses,
140+
[
141+
factory.createPropertyDeclaration(
142+
[],
143+
'scene',
144+
undefined,
145+
undefined,
146+
factory.createIdentifier('Experience'),
147+
),
148+
...node.members,
149+
],
150+
)
151+
.getFullText(),
152+
);
153+
if (appComponentTemplateContent) {
154+
const updatedTemplateContent =
155+
generateExperience === 'append'
156+
? `
157+
${appComponentTemplateContent}
158+
<ngt-canvas [sceneGraph]="scene" />`
159+
: `<ngt-canvas [sceneGraph]="scene" />`;
160+
tree.write(appComponentTemplatePath, updatedTemplateContent);
161+
} else {
162+
updatedContent = tsquery.replace(
163+
updatedContent,
164+
'Identifier[name="template"] ~ NoSubstitutionTemplateLiteral',
165+
(node: NoSubstitutionTemplateLiteral) => {
166+
return generateExperience === 'append'
167+
? `
168+
${node.getFullText()}
169+
<ngt-canvas [sceneGraph]="scene" />`
170+
: `<ngt-canvas [sceneGraph]="scene" />`;
171+
},
172+
);
173+
}
174+
175+
tree.write(appComponentPath, updatedContent);
176+
}
177+
} else {
178+
}
179+
} else {
180+
logger.warn(`
181+
"sourceRoot" not found. Angular Three was initialized successfully but an Experience component was not generated.`);
182+
}
183+
}
184+
33185
return () => {
34186
installPackagesTask(tree);
35187
};

libs/plugin/src/generators/init/schema.json

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,16 @@
22
"$schema": "http://json-schema.org/schema",
33
"cli": "nx",
44
"$id": "Init",
5-
"title": "Init Angular Three"
5+
"title": "Init Angular Three",
6+
"properties": {
7+
"project": {
8+
"type": "string",
9+
"description": "The name of the project to add the Tailwind CSS setup for.",
10+
"$default": {
11+
"$source": "argv",
12+
"index": 0
13+
},
14+
"x-prompt": "What project would you like to add the Angular Three setup?"
15+
}
16+
}
617
}

package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
"@nx/vite": "16.7.4",
3636
"@nx/web": "16.7.4",
3737
"@nx/workspace": "16.7.4",
38+
"@phenomnomnominal/tsquery": "^6.1.3",
3839
"@release-it/bumper": "^5.1.0",
3940
"@release-it/conventional-changelog": "^7.0.0",
4041
"@schematics/angular": "16.2.0",
@@ -57,6 +58,7 @@
5758
"@typescript-eslint/parser": "6.4.1",
5859
"autoprefixer": "^10.4.15",
5960
"dotenv-cli": "^7.3.0",
61+
"enquirer": "^2.4.1",
6062
"eslint": "8.48.0",
6163
"eslint-config-prettier": "9.0.0",
6264
"glsl-noise": "^0.0.0",

pnpm-lock.yaml

Lines changed: 28 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)