Skip to content

Commit ca38f8e

Browse files
committed
setup generate soba json
1 parent f59c082 commit ca38f8e

File tree

5 files changed

+323
-179
lines changed

5 files changed

+323
-179
lines changed

libs/soba/abstractions/src/billboard/billboard.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ export type NgtsBillboardState = {
1313

1414
declare global {
1515
interface HTMLElementTagNameMap {
16+
/**
17+
* @extends ngt-group
18+
*/
1619
'ngts-billboard': NgtsBillboardState & NgtGroup;
1720
}
1821
}

libs/soba/project.json

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
"tags": [],
77
"projectType": "library",
88
"targets": {
9-
"package": {
9+
"build": {
1010
"executor": "@nx/angular:package",
1111
"outputs": ["{workspaceRoot}/dist/{projectRoot}"],
1212
"options": {
@@ -22,6 +22,14 @@
2222
},
2323
"defaultConfiguration": "production"
2424
},
25+
26+
"package": {
27+
"executor": "nx:run-commands",
28+
"options": {
29+
"commands": ["pnpm exec nx build soba", "node ./tools/scripts/generate-soba-json.mjs"],
30+
"parallel": false
31+
}
32+
},
2533
"test": {
2634
"executor": "@nx/jest:jest",
2735
"outputs": ["{workspaceRoot}/coverage/{projectRoot}"],

tools/scripts/generate-json.mjs

Lines changed: 6 additions & 178 deletions
Original file line numberDiff line numberDiff line change
@@ -1,185 +1,20 @@
1-
import { createWriteStream, existsSync, mkdirSync, unlinkSync, writeFileSync } from 'fs';
21
import ts from 'typescript';
3-
import { format } from 'util';
2+
import { commonAttributes, createBareJsons, createLog, createProgram } from './utils.mjs';
43

5-
if (existsSync('tmp/log.txt')) {
6-
unlinkSync('tmp/log.txt');
7-
}
8-
9-
const logFile = createWriteStream('tmp/log.txt', { flags: 'a' });
10-
// Or 'w' to truncate the file every time the process starts.
11-
const logStdout = process.stdout;
12-
13-
function writeToLog() {
14-
logFile.write(format.apply(null, [`------${new Date().toLocaleDateString()}------`, '\n']));
15-
// Storing without color codes
16-
logFile.write(format.apply(null, arguments).replace(/\033\[[0-9;]*m/g, '') + '\n');
17-
// Display normally, with colors to Stdout
18-
logStdout.write(format.apply(null, arguments) + '\n');
19-
}
20-
21-
const TYPE_CHECKER_FLAGS =
22-
ts.NodeBuilderFlags.NoTruncation | ts.NodeBuilderFlags.InTypeAlias | ts.NodeBuilderFlags.NoTypeReduction;
23-
24-
/**
25-
* @param {ts.Type} type
26-
* @param {ts.Node} [node]
27-
*
28-
* @return {ts.TypeNode}
29-
*/
30-
function typeToTypeNode(type, node) {
31-
return typeChecker.typeToTypeNode(type, node, TYPE_CHECKER_FLAGS);
32-
}
33-
34-
/**
35-
* @param {ts.Type} type
36-
* @param {ts.Node} [node]
37-
*
38-
* @return {string}
39-
*/
40-
function typeToString(type, node) {
41-
return typeChecker.typeToString(type, node, TYPE_CHECKER_FLAGS);
42-
}
4+
const logger = createLog();
435

44-
const metadataJson = {
45-
$schema: 'https://raw.githubusercontent.com/microsoft/vscode-html-languageservice/main/docs/customData.schema.json',
46-
version: 1.1,
47-
tags: [],
48-
};
49-
const commonAttributes = [
50-
{
51-
name: 'ngtCompound',
52-
description: 'Annotation that this is a compounded element',
53-
},
54-
{
55-
name: 'attach',
56-
description: 'Property to attach to parent. Can be dotted path',
57-
},
58-
{
59-
name: '[attach]',
60-
description: 'An array of paths to attach to parent. Can also be an NgtAttachFunction',
61-
},
62-
{
63-
name: '[ref]',
64-
description: 'Assign an NgtInjectedRef',
65-
},
66-
{
67-
name: '(beforeRender)',
68-
description: 'Register an event to be run in animation loop',
69-
},
70-
{
71-
name: '(afterAttach)',
72-
description: 'Register an event to be invoked after this node is attached to the parent',
73-
},
74-
{
75-
name: '(afterUpdate)',
76-
description: "Register an event to be invoked after this node's properties are updated",
77-
},
78-
];
79-
const webTypesJson = {
80-
$schema: 'https://raw.githubusercontent.com/JetBrains/web-types/master/schema/web-types.json',
81-
version: '2.0',
82-
name: 'angular-three',
83-
framework: 'angular',
84-
'js-types-syntax': 'typescript',
85-
'framework-config': {
86-
'enable-when': {
87-
'node-packages': ['angular-three'],
88-
},
89-
},
90-
contributions: {
91-
html: {
92-
attributes: commonAttributes,
93-
elements: [],
94-
},
95-
},
96-
};
6+
const { metadataJson, webTypesJson, write } = createBareJsons();
977

98-
const program = ts.createProgram(['libs/core/src/lib/three-types.ts'], {
99-
module: ts.ModuleKind.ESNext,
100-
target: ts.ModuleKind.ESNext,
101-
strict: true,
102-
emitDeclarationOnly: true,
103-
});
104-
const typeChecker = program.getTypeChecker();
105-
const sourceFile = program.getSourceFile('libs/core/src/lib/three-types.ts');
8+
const { typeChecker, sourceFile, typeToTypeNode, typeToString, processIntersectionTypeNode, processTypeMembers } =
9+
createProgram(['libs/core/src/lib/three-types.ts']);
10610

10711
/** @type {Map<string, { typeDeclaration: ts.TypeAliasDeclaration, type: ts.Type, typeNode: ts.TypeNode, typeString: string}>} */
10812
const typeDeclarationMap = new Map();
10913

110-
const PROPERTIES_TO_SKIP = [
111-
'__ngt_args__',
112-
'type',
113-
'uuid',
114-
'toJSON',
115-
'clone',
116-
'copy',
117-
'dispatchEvent',
118-
'addEventListener',
119-
'removeEventListener',
120-
'fromArray',
121-
'toArray',
122-
];
123-
const skipIs = (str) => str.startsWith('is');
124-
const skipAction = (str) =>
125-
str.startsWith('set') ||
126-
str.startsWith('add') ||
127-
str.startsWith('has') ||
128-
str.startsWith('apply') ||
129-
str.startsWith('update') ||
130-
str.startsWith('_on') ||
131-
str.startsWith('get');
132-
const overlapWithCommonAttributes = (str) =>
133-
commonAttributes.some((attr) => attr.name === str || attr.name === `[${str}]`);
134-
13514
const THREE_MEMBERS_TO_SKIP = ['ngt-primitive', 'ngt-value'];
13615
const THREE_ELEMENTS_NAME = 'ThreeElements';
13716
const THREE_OBJECT_EVENTS_MAP_NAME = 'NgtObject3DEventsMap';
13817

139-
/**
140-
* @param {{name: string, attributes: any[]}} metadata
141-
* @param {ts.NodeArray<ts.TypeElement>} members
142-
*/
143-
function processTypeMembers(metadata, members) {
144-
if (!members?.length) return;
145-
for (const member of members) {
146-
/** @type {string} */
147-
const memberName = member.name?.text || member.name?.escapedText;
148-
if (
149-
!memberName ||
150-
PROPERTIES_TO_SKIP.includes(memberName) ||
151-
skipIs(memberName) ||
152-
skipAction(memberName) ||
153-
overlapWithCommonAttributes(memberName)
154-
) {
155-
continue;
156-
}
157-
metadata.attributes.push({ name: memberName }, { name: `[${memberName}]` });
158-
}
159-
}
160-
161-
/**
162-
* @param {{name: string, attributes: any[]}} metadata
163-
* @param {ts.IntersectionTypeNode} typeNode
164-
*/
165-
function processIntersectionTypeNode(metadata, typeNode) {
166-
for (const type of typeNode.types) {
167-
if (ts.isTypeReferenceNode(type)) {
168-
// TODO: we don't know how to get the inheritance of some THREE object without turning the source file into an AST
169-
// writeToLog(
170-
// 'type reference -->',
171-
// type.typeName,
172-
// type.typeArguments[0].typeArguments[0].typeArguments[0].typeArguments[0].typeArguments[0].typeName.right
173-
// .symbol
174-
// ); THREE.Light, THREE.Mesh, THREE.SpotLight
175-
} else if (ts.isTypeLiteralNode(type)) {
176-
// this is the type literal that we pass in as an second type argument to NgtOverwrite for NgtObject3DNode
177-
// so we'll process it as well
178-
processTypeMembers(metadata, type.members);
179-
}
180-
}
181-
}
182-
18318
/**
18419
* @param {{name: string, attributes: any[]}} metadata
18520
*/
@@ -254,11 +89,4 @@ ts.forEachChild(sourceFile, (node) => {
25489
}
25590
});
25691

257-
writeFileSync('dist/libs/core/metadata.json', JSON.stringify(metadataJson));
258-
writeFileSync('dist/libs/core/web-types.json', JSON.stringify(webTypesJson));
259-
260-
if (!existsSync('node_modules/angular-three')) {
261-
mkdirSync('node_modules/angular-three');
262-
}
263-
writeFileSync('node_modules/angular-three/metadata.json', JSON.stringify(metadataJson));
264-
writeFileSync('node_modules/angular-three/web-types.json', JSON.stringify(webTypesJson));
92+
write(metadataJson, webTypesJson);

tools/scripts/generate-soba-json.mjs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import devkit from '@nx/devkit';
2+
import { execSync } from 'child_process';
3+
import { existsSync } from 'fs';
4+
import { join } from 'path';
5+
import ts from 'typescript';
6+
import { createBareJsons, createProgram } from './utils.mjs';
7+
8+
const coreMetadataJsonPath = 'node_modules/angular-three/metadata.json';
9+
10+
if (!existsSync(coreMetadataJsonPath)) {
11+
execSync(`node tools/scripts/generate-json.mjs`, { cwd: '.' });
12+
}
13+
14+
const coreMetadataJson = devkit.readJsonFile(coreMetadataJsonPath);
15+
const coreTags = coreMetadataJson.tags;
16+
17+
const sobaRoot = 'libs/soba';
18+
19+
const abstractionsRoot = 'abstractions/src';
20+
const abstractions = ['billboard/billboard.ts'].map((path) => join(sobaRoot, abstractionsRoot, path));
21+
22+
const paths = [...abstractions];
23+
24+
const { metadataJson, webTypesJson, write } = createBareJsons('angular-three-soba', 'soba');
25+
26+
for (const path of paths) {
27+
const { sourceFile, processIntersectionTypeNode } = createProgram([path]);
28+
29+
ts.forEachChild(sourceFile, (node) => {
30+
if (ts.isModuleDeclaration(node)) {
31+
const nodeBody = node.body;
32+
if (ts.isModuleBlock(nodeBody)) {
33+
const statement = nodeBody.statements[0];
34+
if (ts.isInterfaceDeclaration(statement)) {
35+
for (const member of statement.members) {
36+
if (ts.isPropertySignature(member)) {
37+
const metadataAtMember = { name: member.name.text, attributes: [] };
38+
39+
/**
40+
* @type {ts.JSDocComment[]}
41+
*/
42+
const jsDocs = member['jsDoc'] || [];
43+
const jsDocComment = jsDocs[0];
44+
45+
if (jsDocComment) {
46+
for (const tag of jsDocComment.tags) {
47+
if (ts.isJSDocAugmentsTag(tag)) {
48+
const extendTag = tag.class.expression.escapedText;
49+
const foundCoreTag = coreTags.find((coreTag) => coreTag.name === extendTag);
50+
if (foundCoreTag) {
51+
metadataAtMember.attributes.push(...foundCoreTag.attributes);
52+
}
53+
}
54+
}
55+
}
56+
57+
const memberType = member.type;
58+
59+
if (ts.isIntersectionTypeNode(memberType)) {
60+
processIntersectionTypeNode(metadataAtMember, memberType);
61+
}
62+
63+
metadataJson.tags.push(metadataAtMember);
64+
webTypesJson.contributions.html.elements.push(metadataAtMember);
65+
}
66+
}
67+
}
68+
}
69+
}
70+
});
71+
}
72+
73+
write(metadataJson, webTypesJson);

0 commit comments

Comments
 (0)