├── .eslintignore
├── .github
└── workflows
│ ├── ci.yml
│ └── codeql-analysis.yml
├── .gitignore
├── .prettierignore
├── .prettierrc.json
├── .vscode
└── settings.json
├── AUTHORS.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── eslint.config.js
├── example-1.gif
├── example-1.mp4
├── jest.config.js
├── package-lock.json
├── package.json
├── packages
├── schema-dts-gen
│ ├── LICENSE
│ ├── README.md
│ ├── bin
│ │ └── schema-dts-gen.js
│ ├── jest.config.js
│ ├── package.json
│ ├── src
│ │ ├── cli
│ │ │ ├── args.ts
│ │ │ ├── cli.ts
│ │ │ └── internal
│ │ │ │ └── main.ts
│ │ ├── dep-types
│ │ │ ├── rehype-minify-whitespace.d.ts
│ │ │ └── remark-wiki-link.d.ts
│ │ ├── index.ts
│ │ ├── logging
│ │ │ └── index.ts
│ │ ├── transform
│ │ │ ├── toClass.ts
│ │ │ ├── toEnum.ts
│ │ │ ├── toProperty.ts
│ │ │ └── transform.ts
│ │ ├── triples
│ │ │ ├── operators.ts
│ │ │ ├── reader.ts
│ │ │ ├── term_utils.ts
│ │ │ └── wellKnown.ts
│ │ ├── ts
│ │ │ ├── action_constraints.ts
│ │ │ ├── class.ts
│ │ │ ├── context.ts
│ │ │ ├── enum.ts
│ │ │ ├── helper_types.ts
│ │ │ ├── property.ts
│ │ │ └── util
│ │ │ │ ├── arrayof.ts
│ │ │ │ ├── comments.ts
│ │ │ │ ├── names.ts
│ │ │ │ └── union.ts
│ │ ├── tsconfig.json
│ │ └── util
│ │ │ └── assert.ts
│ ├── test
│ │ ├── baselines
│ │ │ ├── category_test.ts
│ │ │ ├── comments_test.ts
│ │ │ ├── data_type_union_test.ts
│ │ │ ├── default_ontology_test.ts
│ │ │ ├── deprecated_objects_test.ts
│ │ │ ├── duplicate_comments_test.ts
│ │ │ ├── enum_skipped_test.ts
│ │ │ ├── inheritance_multiple_test.ts
│ │ │ ├── inheritance_one_test.ts
│ │ │ ├── invalid_schemas_test.ts
│ │ │ ├── must_define_datatype_test.ts
│ │ │ ├── nested_datatype_test.ts
│ │ │ ├── nodeprecated_objects_test.ts
│ │ │ ├── owl_mixed_basic_test.ts
│ │ │ ├── property_edge_cases_test.ts
│ │ │ ├── quantities_test.ts
│ │ │ ├── role_inheritance_test.ts
│ │ │ ├── role_simple_test.ts
│ │ │ ├── simple_test.ts
│ │ │ ├── sorted_enum_test.ts
│ │ │ ├── sorted_props_test.ts
│ │ │ ├── sorted_proptypes_test.ts
│ │ │ ├── stringlike_http_test.ts
│ │ │ ├── stringlike_https_test.ts
│ │ │ ├── surgical_procedure_3_4_test.ts
│ │ │ ├── surgical_procedure_3_4_v2_test.ts
│ │ │ ├── surgical_procedure_test.ts
│ │ │ ├── transitive_class_test.ts
│ │ │ ├── unknown_wellknown_test.ts
│ │ │ └── url_test.ts
│ │ ├── cli
│ │ │ ├── args_logmessages_test.ts
│ │ │ └── args_test.ts
│ │ ├── helpers
│ │ │ ├── async.ts
│ │ │ ├── main_driver.ts
│ │ │ └── make_class.ts
│ │ ├── logging
│ │ │ └── index_test.ts
│ │ ├── triples
│ │ │ ├── reader_test.ts
│ │ │ └── wellKnown_test.ts
│ │ ├── ts
│ │ │ ├── class_test.ts
│ │ │ ├── context_test.ts
│ │ │ ├── enum_test.ts
│ │ │ ├── names_test.ts
│ │ │ └── property_test.ts
│ │ └── tsconfig.json
│ └── tsconfig.json
└── schema-dts
│ ├── .gitignore
│ ├── LICENSE
│ ├── README.md
│ ├── package.json
│ ├── test
│ ├── easy.ts
│ ├── graph.ts
│ ├── tsconfig.json
│ ├── values.ts
│ ├── withactionconstraints.ts
│ └── withcontext.ts
│ └── tsconfig.json
└── tsconfig-base.json
/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | coverage
3 | jest.config.js
4 | schema-dts/lib/
5 | schema-dts/dist/
6 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: Node.js CI
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | pull_request:
7 | branches: [ main ]
8 |
9 | jobs:
10 | build:
11 |
12 | runs-on: ubuntu-latest
13 |
14 | strategy:
15 | matrix:
16 | node-version: [22.x, 23.x]
17 |
18 | steps:
19 | - uses: actions/checkout@v2
20 | - name: Use Node.js ${{ matrix.node-version }}
21 | uses: actions/setup-node@v2
22 | with:
23 | node-version: ${{ matrix.node-version }}
24 | - run: npm ci
25 | - run: npm run build
26 | - run: npm test
27 | - name: Coveralls - schema-dts-gen
28 | uses: coverallsapp/github-action@master
29 | if: ${{ matrix.node-version == '22.x' }}
30 | with:
31 | github-token: ${{ secrets.GITHUB_TOKEN }}
32 | path-to-lcov: ./packages/schema-dts-gen/coverage/lcov.info
33 | base-path: packages/schema-dts-gen
34 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | name: "CodeQL"
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | pull_request:
7 | # The branches below must be a subset of the branches above
8 | branches: [ main ]
9 | schedule:
10 | - cron: '17 11 * * 6'
11 |
12 | jobs:
13 | analyze:
14 | name: Analyze
15 | runs-on: ubuntu-latest
16 | permissions:
17 | actions: read
18 | contents: read
19 | security-events: write
20 |
21 | strategy:
22 | fail-fast: false
23 | matrix:
24 | language: [ 'javascript' ]
25 |
26 | steps:
27 | - name: Checkout repository
28 | uses: actions/checkout@v2
29 |
30 | # Initializes the CodeQL tools for scanning.
31 | - name: Initialize CodeQL
32 | uses: github/codeql-action/init@v2
33 | with:
34 | languages: ${{ matrix.language }}
35 | # If you wish to specify custom queries, you can do so here or in a config file.
36 | # By default, queries listed here will override any specified in a config file.
37 | # Prefix the list here with "+" to use these queries and those in the config file.
38 | # queries: ./path/to/local/query, your-org/your-repo/queries@main
39 |
40 | # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
41 | # If this step fails, then you should remove it and run the build manually (see below)
42 | - name: Autobuild
43 | uses: github/codeql-action/autobuild@v2
44 |
45 | # ℹ️ Command-line programs to run using the OS shell.
46 | # 📚 https://git.io/JvXDl
47 |
48 | # ✏️ If the Autobuild fails above, remove it and uncomment the following three lines
49 | # and modify them (or add more) to build your code if your project
50 | # uses a compiled language
51 |
52 | #- run: |
53 | # make bootstrap
54 | # make release
55 |
56 | - name: Perform CodeQL Analysis
57 | uses: github/codeql-action/analyze@v2
58 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | built
3 | dist/
4 | .nyc_output/
5 | coverage/
6 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | dist/
3 | packages/schema-dts/lib/
4 | .nyc_output/
5 | coverage/
6 | .github/
7 |
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "proseWrap": "always",
3 | "singleQuote": true,
4 | "bracketSpacing": false,
5 | "arrowParens": "avoid"
6 | }
7 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.formatOnSave": true,
3 | "editor.tabSize": 2,
4 | "eslint.options": {
5 | "overrideConfigFile": "./.eslintrc.cjs"
6 | },
7 | "editor.defaultFormatter": "esbenp.prettier-vscode",
8 | "[typescript]": {
9 | "editor.defaultFormatter": "esbenp.prettier-vscode"
10 | },
11 | "typescript.tsdk": "node_modules/typescript/lib"
12 | }
13 |
--------------------------------------------------------------------------------
/AUTHORS.md:
--------------------------------------------------------------------------------
1 | Schema DTS and the Schema TypeScript Generator are maintained by:
2 |
3 | - [Eyas Sharaiha](https://eyas.sh/)
4 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to Contribute
2 |
3 | We'd love to accept your patches and contributions to this project. There are
4 | just a few small guidelines you need to follow.
5 |
6 | ## Contributor License Agreement
7 |
8 | Contributions to this project must be accompanied by a Contributor License
9 | Agreement. You (or your employer) retain the copyright to your contribution;
10 | this simply gives us permission to use and redistribute your contributions as
11 | part of the project. Head over to to see
12 | your current agreements on file or to sign a new one.
13 |
14 | You generally only need to submit a CLA once, so if you've already submitted one
15 | (even if it was for a different project), you probably don't need to do it
16 | again.
17 |
18 | ## Code reviews
19 |
20 | All submissions, including submissions by project members, require review. We
21 | use GitHub pull requests for this purpose. Consult
22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
23 | information on using pull requests.
24 |
25 | ## Community Guidelines
26 |
27 | This project follows
28 | [Google's Open Source Community Guidelines](https://opensource.google.com/conduct/).
29 |
--------------------------------------------------------------------------------
/eslint.config.js:
--------------------------------------------------------------------------------
1 | import jsdoc from 'eslint-plugin-jsdoc';
2 | import importPlugin from 'eslint-plugin-import';
3 | import prettierConfig from 'eslint-config-prettier';
4 | import stylistic from '@stylistic/eslint-plugin';
5 | import tseslint from 'typescript-eslint';
6 |
7 | export default tseslint.config(
8 | tseslint.configs.recommendedTypeChecked,
9 | prettierConfig,
10 | {
11 | languageOptions: {
12 | parserOptions: {
13 | projectService: true,
14 | tsconfigRootDir: [import.meta.dirname],
15 | },
16 | },
17 | },
18 | {
19 | files: ['**/*.ts'],
20 | plugins: {jsdoc, import: importPlugin, '@stylistic': stylistic},
21 | rules: {
22 | '@typescript-eslint/array-type': [
23 | 'error',
24 | {
25 | default: 'array-simple',
26 | },
27 | ],
28 | '@typescript-eslint/no-restricted-types': [
29 | 'error',
30 | {
31 | types: {
32 | Object: {
33 | fixWith: 'object',
34 | },
35 | String: {
36 | fixWith: 'string',
37 | },
38 | Number: {
39 | fixWith: 'number',
40 | },
41 | Boolean: {
42 | fixWith: 'boolean',
43 | },
44 | },
45 | },
46 | ],
47 | '@typescript-eslint/naming-convention': [
48 | 'error',
49 | {
50 | selector: 'default',
51 | format: ['camelCase'],
52 | leadingUnderscore: 'allow',
53 | trailingUnderscore: 'allow',
54 | },
55 | {
56 | selector: 'variable',
57 | format: ['PascalCase', 'camelCase', 'UPPER_CASE'],
58 | modifiers: ['const'],
59 | leadingUnderscore: 'allow',
60 | trailingUnderscore: 'allow',
61 | },
62 | {
63 | selector: 'variableLike',
64 | format: ['PascalCase', 'camelCase'],
65 | filter: {regex: '^_$', match: false},
66 | },
67 | {
68 | selector: 'memberLike',
69 | format: ['camelCase', 'PascalCase'],
70 | },
71 | {
72 | selector: 'memberLike',
73 | modifiers: ['private'],
74 | format: ['camelCase', 'PascalCase'],
75 | leadingUnderscore: 'allow',
76 | },
77 | {
78 | selector: 'typeLike',
79 | format: ['PascalCase'],
80 | },
81 | {
82 | selector: ['typeProperty', 'objectLiteralProperty'],
83 | format: null,
84 | },
85 | ],
86 | '@typescript-eslint/consistent-type-assertions': 'error',
87 | '@typescript-eslint/consistent-type-definitions': 'error',
88 | '@typescript-eslint/explicit-member-accessibility': [
89 | 'error',
90 | {
91 | accessibility: 'no-public',
92 | },
93 | ],
94 | '@stylistic/member-delimiter-style': [
95 | 'error',
96 | {
97 | multiline: {
98 | delimiter: 'semi',
99 | requireLast: true,
100 | },
101 | singleline: {
102 | delimiter: 'semi',
103 | requireLast: false,
104 | },
105 | },
106 | ],
107 | '@typescript-eslint/no-explicit-any': 'error',
108 | '@typescript-eslint/no-inferrable-types': 'error',
109 | '@typescript-eslint/no-namespace': 'error',
110 | '@typescript-eslint/no-require-imports': 'error',
111 | '@typescript-eslint/no-unused-expressions': [
112 | 'error',
113 | {allowShortCircuit: true},
114 | ],
115 | '@typescript-eslint/no-unused-vars': [
116 | 'error',
117 | {
118 | argsIgnorePattern: '^_',
119 | caughtErrorsIgnorePattern: '^_',
120 | destructuredArrayIgnorePattern: '^_',
121 | varsIgnorePattern: '^_',
122 | ignoreRestSiblings: true,
123 | },
124 | ],
125 | '@typescript-eslint/triple-slash-reference': 'error',
126 | '@stylistic/semi': ['error', 'always'],
127 | 'arrow-body-style': 'error',
128 | curly: ['error', 'multi-line'],
129 | 'default-case': 'error',
130 | eqeqeq: ['error', 'smart'],
131 | 'guard-for-in': 'error',
132 | 'id-denylist': [
133 | 'error',
134 | 'any',
135 | 'Number',
136 | 'number',
137 | 'String',
138 | 'string',
139 | 'Boolean',
140 | 'boolean',
141 | 'Undefined',
142 | ],
143 | 'id-match': 'error',
144 | 'import/no-default-export': 'error',
145 | 'jsdoc/check-alignment': 'error',
146 | 'new-parens': 'error',
147 | 'no-debugger': 'error',
148 | 'no-duplicate-case': 'error',
149 | 'no-new-wrappers': 'error',
150 | 'no-return-await': 'error',
151 | 'no-throw-literal': 'error',
152 | 'no-underscore-dangle': 'off',
153 | 'no-unsafe-finally': 'error',
154 | 'no-unused-expressions': ['error', {allowShortCircuit: true}],
155 | 'no-unused-labels': 'error',
156 | 'no-var': 'error',
157 | 'object-shorthand': 'error',
158 | 'prefer-arrow-callback': 'error',
159 | 'prefer-const': 'error',
160 | radix: 'error',
161 | 'use-isnan': 'error',
162 | },
163 | settings: {},
164 | },
165 | );
166 |
--------------------------------------------------------------------------------
/example-1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/schema-dts/966fffa899ef4d70da453268ccf87f75149cd52d/example-1.gif
--------------------------------------------------------------------------------
/example-1.mp4:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/schema-dts/966fffa899ef4d70da453268ccf87f75149cd52d/example-1.mp4
--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | projects: ['/packages/*'],
3 | };
4 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "monorepo",
3 | "private": "true",
4 | "author": "Eyas Sharaiha (https://eyas.sh/)",
5 | "maintainers": [
6 | "Eyas Sharaiha (https://eyas.sh/)"
7 | ],
8 | "devDependencies": {
9 | "@jest/globals": "^29.7.0",
10 | "@stylistic/eslint-plugin": "^4.2.0",
11 | "eslint": "^9.21.0",
12 | "eslint-config-prettier": "^10.0.2",
13 | "eslint-plugin-import": "^2.31.0",
14 | "eslint-plugin-jsdoc": "^50.6.17",
15 | "jest": "^29.7.0",
16 | "prettier": "^3.5.3",
17 | "rimraf": "^6.0.1",
18 | "ts-jest": "^29.3.4",
19 | "typescript": "^5.8.2",
20 | "typescript-eslint": "^8.32.1"
21 | },
22 | "overrides": {
23 | "typescript-eslint": {
24 | "typescript": "$typescript"
25 | },
26 | "@typescript-eslint/eslint-plugin": {
27 | "typescript": "$typescript"
28 | },
29 | "@typescript-eslint/parser": {
30 | "typescript": "$typescript"
31 | }
32 | },
33 | "engines": {
34 | "node": ">=14.0.0",
35 | "npm": ">=7.0.0"
36 | },
37 | "engineStrict": true,
38 | "nyc": {
39 | "extension": [
40 | ".ts",
41 | ".tsx"
42 | ],
43 | "exclude": [
44 | "**/*.d.ts",
45 | "dist/**/*",
46 | "coverage/**/*",
47 | "test/**/*",
48 | "src/cli/cli.ts"
49 | ],
50 | "reporter": [
51 | "html"
52 | ],
53 | "all": true
54 | },
55 | "homepage": "https://opensource.google/projects/schema-dts",
56 | "bugs": "https://github.com/google/schema-dts/issues",
57 | "repository": "github:google/schema-dts",
58 | "license": "Apache-2.0",
59 | "scripts": {
60 | "clean": "rimraf \"packages/**/dist\" \"packages/**/*.tsbuildinfo\" packages/schema-dts/lib/*",
61 | "build": "npm run build --workspaces",
62 | "lint:prettier": "prettier --check .",
63 | "lint:eslint": "npm run lint:eslint --workspaces --if-present --",
64 | "lint": "npm run lint:eslint && npm run lint:prettier",
65 | "fix:prettier": "prettier --write .",
66 | "fix:eslint": "npm run lint:eslint -- --fix ",
67 | "fix": "npm run fix:eslint && npm run fix:prettier",
68 | "test": "npm run lint && npm run test --workspaces",
69 | "coverage_on_travis": "npm run coverage_on_travis -w schema-dts-gen",
70 | "pkg": "npm run clean && npm run build"
71 | },
72 | "type": "module",
73 | "workspaces": [
74 | "packages/schema-dts-gen",
75 | "packages/schema-dts"
76 | ]
77 | }
78 |
--------------------------------------------------------------------------------
/packages/schema-dts-gen/README.md:
--------------------------------------------------------------------------------
1 | [](https://github.com/google/schema-dts/actions/workflows/ci.yml)
2 | [](https://coveralls.io/github/google/schema-dts?branch=main)
3 | [](https://www.npmjs.com/package/schema-dts-gen)
4 |
5 | # schema-dts-gen
6 |
7 | JSONG-LD TypeScript typing generator for Schema.org vocabulary & related
8 | ontologies.
9 |
10 | **schema-dts-gen** is the generator that powers
11 | [schema-dts](https://www.npmjs.com/package/schema-dts). It creates TypeScript
12 | definitions for JSON-LD conforming to a given ontology.
13 |
14 | Note: This is not an officially supported Google product.
15 |
16 | ## Usage
17 |
18 | To use the typings for your project, simply add the
19 | [`schema-dts`](https://www.npmjs.com/package/schema-dts) NPM package to your
20 | project:
21 |
22 | ```command
23 | npm install schema-dts-gen
24 | npx schema-dts-gen --ontology=https://schema.org/version/latest/schemaorg-all-https.nt
25 | ```
26 |
27 | Command line usage:
28 |
29 | - **Specify your ontology**
30 |
31 | - Specify **`--ontology`**: An HTTPs URL to an .nt NTriple file declaring your
32 | ontology.
33 |
34 | Must be compatible with Schema.org, including the Schema.org `DataType`s and
35 | specifying a top-level `Thing` type.
36 |
37 | - **`--context`**: Defaults to `https://schema.org`, the value or values to be
38 | used with the `"@context"` property.
39 |
40 | Can be either a single URL, or a comma separated list of two or more name:URL
41 | pairs.
42 |
43 | The context affects names of string properties in types, as well as the values
44 | of an object's `"@type"`.
45 |
46 | - **`--deprecated`**/**`--nodeprecated`**: Whether or not to include deprecated
47 | Schema.org types and properties. When included, these types will still be
48 | marked with `@deprecated` JSDOC tags.
49 |
50 | - **`--verbose`**: Outputs additional logs and debugging notes to stderr.
51 |
--------------------------------------------------------------------------------
/packages/schema-dts-gen/bin/schema-dts-gen.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | import '../dist/src/cli/cli.js';
3 |
--------------------------------------------------------------------------------
/packages/schema-dts-gen/jest.config.js:
--------------------------------------------------------------------------------
1 | export default {
2 | preset: 'ts-jest/presets/default-esm', // or other ESM presets
3 | moduleNameMapper: {
4 | '^(\\.{1,2}/.*)\\.js$': '$1',
5 | },
6 | prettierPath: null,
7 | testEnvironment: 'node',
8 | testMatch: ['**/*_test.[jt]s?(x)'],
9 | testPathIgnorePatterns: ['/node_modules/', '/dist/'],
10 | collectCoverage: true,
11 | coveragePathIgnorePatterns: ['/node_modules/', '/dist/'],
12 | // We don't need this because we use Coveralls 'base-dir'
13 | // coverageReporters: ['json', ['lcov', {projectRoot: '../../'}], 'text'],
14 | };
15 |
--------------------------------------------------------------------------------
/packages/schema-dts-gen/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "schema-dts-gen",
3 | "version": "1.1.5",
4 | "displayName": "schema-dts Generator",
5 | "description": "Generate TypeScript Definitions for Schema.org Schema",
6 | "author": "Eyas Sharaiha (https://eyas.sh/)",
7 | "maintainers": [
8 | "Eyas Sharaiha (https://eyas.sh/)"
9 | ],
10 | "contributors": [
11 | "Eyas Sharaiha (https://eyas.sh/)"
12 | ],
13 | "files": [
14 | "dist/src/**/*"
15 | ],
16 | "types": "./dist/src/index.d.ts",
17 | "main": "./dist/src/index.js",
18 | "bin": {
19 | "schema-dts-gen": "bin/schema-dts-gen.js"
20 | },
21 | "sideEffects": false,
22 | "type": "module",
23 | "devDependencies": {
24 | "@jest/globals": "^29.4.3",
25 | "@types/argparse": "^2.0.17",
26 | "@types/array.prototype.flatmap": "^1.2.6",
27 | "@types/jest": "^29.5.14",
28 | "@types/n3": "^1.21.1",
29 | "@types/node": "^22.13.8",
30 | "@types/unist": "^3.0.3",
31 | "cross-env": "^7.0.3"
32 | },
33 | "dependencies": {
34 | "argparse": "^2.0.1",
35 | "n3": "^1.23.1",
36 | "rehype-minify-whitespace": "^6.0.2",
37 | "rehype-parse": "^9.0.1",
38 | "rehype-raw": "^7.0.0",
39 | "remark-parse": "^11.0.0",
40 | "remark-rehype": "^11.1.1",
41 | "remark-wiki-link": "^2.0.1",
42 | "unified": "^11.0.5"
43 | },
44 | "engines": {
45 | "node": ">=14.0.0"
46 | },
47 | "engineStrict": true,
48 | "peerDependencies": {
49 | "typescript": ">=4.9.5"
50 | },
51 | "nyc": {
52 | "extension": [
53 | ".ts",
54 | ".tsx"
55 | ],
56 | "exclude": [
57 | "**/*.d.ts",
58 | "dist/**/*",
59 | "coverage/**/*",
60 | "test/**/*",
61 | "src/cli/cli.ts"
62 | ],
63 | "reporter": [
64 | "html"
65 | ],
66 | "all": true
67 | },
68 | "keywords": [
69 | "typescript",
70 | "tsd",
71 | "dts",
72 | "schema.org",
73 | "Semantic Web",
74 | "semantic-web",
75 | "Linked Data",
76 | "linked-data",
77 | "jsonld",
78 | "JSON-LD",
79 | "structured data",
80 | "structured-data"
81 | ],
82 | "homepage": "https://opensource.google/projects/schema-dts",
83 | "bugs": "https://github.com/google/schema-dts/issues",
84 | "repository": {
85 | "type": "git",
86 | "url": "https://github.com/google/schema-dts.git",
87 | "directory": "packages/schema-dts-gen"
88 | },
89 | "license": "Apache-2.0",
90 | "scripts": {
91 | "lint:eslint": "eslint src/**/*.ts test/**/*.ts",
92 | "test": "cross-env NODE_OPTIONS=--experimental-vm-modules jest --coverage",
93 | "build": "tsc -b"
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/packages/schema-dts-gen/src/cli/args.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2023 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import {ArgumentParser} from 'argparse';
18 |
19 | export interface Options {
20 | /** HTTPS URL to an .nt file defining a custom ontology. */
21 | ontology: string;
22 | file: string | undefined;
23 | verbose: boolean;
24 | deprecated: boolean;
25 | context: string;
26 | }
27 |
28 | // argparse forces snake_case on us.
29 | /* eslint @typescript-eslint/naming-convention: 0 */
30 | /* eslint camelcase: 0 */
31 | export function ParseFlags(args?: string[]): Options {
32 | const parser = new ArgumentParser({
33 | add_help: true,
34 | description: 'schema-dts generator',
35 | });
36 |
37 | const verbose = parser.add_mutually_exclusive_group({required: false});
38 | verbose.add_argument('--verbose', {
39 | default: false,
40 | action: 'store_true',
41 | dest: 'verbose',
42 | });
43 | verbose.add_argument('--noverbose', {action: 'store_false', dest: 'verbose'});
44 |
45 | parser.add_argument('--schema', {
46 | default: undefined,
47 | help: 'Deprecated. Please use --ontology instead.',
48 | metavar: 'version',
49 | dest: 'schema',
50 | type: DeprecatedValue,
51 | });
52 | parser.add_argument('--layer', {
53 | default: undefined,
54 | help: 'Deprecated. Please use --ontology instead.',
55 | metavar: 'name_of_file',
56 | dest: 'layer',
57 | type: DeprecatedValue,
58 | });
59 | parser.add_argument('--context', {
60 | default: 'https://schema.org',
61 | help:
62 | 'Single URL or comma-separated key:value pairs defining the ' +
63 | "intended '@context' for the generated JSON-LD typings. " +
64 | "By default, this is 'https://schema.org`. Alternatively: " +
65 | '--context=rdf:http://www.w3.org/2000/01/rdf-schema,' +
66 | 'schema:https://schema.org\n' +
67 | "would result in 'rdf:' being a prefix for any RDF Schema " +
68 | "property, and 'schema:' being a prefix for any Schema.org property.",
69 | metavar: 'key1:url,key2:url,...',
70 | dest: 'context',
71 | });
72 | parser.add_argument('--ontology', {
73 | default: 'https://schema.org/version/latest/schemaorg-all-https.nt',
74 | help:
75 | 'HTTPS URL to a custom .nt file defining an entirely self-' +
76 | 'sufficient schema. The schema must still be described in terms of ' +
77 | "Schema.org DataTypes, as well as rdf/rdfs concepts like 'type', " +
78 | "'domainIncludes', and 'rangeIncludes'.",
79 | metavar: 'https://url.to/schema.nt',
80 | dest: 'ontology',
81 | });
82 | parser.add_argument('--file', {
83 | default: undefined,
84 | help: 'file path to a .nt file, for using a local ontology file',
85 | dest: 'file',
86 | });
87 |
88 | const deprecated = parser.add_mutually_exclusive_group({required: false});
89 | deprecated.add_argument('--deprecated', {
90 | default: true,
91 | help: 'Include deprecated Classes and Properties.',
92 | action: 'store_true',
93 | dest: 'deprecated',
94 | });
95 | deprecated.add_argument('--nodeprecated', {
96 | help: 'Skip deprecated Classes and Properties.',
97 | action: 'store_false',
98 | dest: 'deprecated',
99 | });
100 | return parser.parse_args(args) as Options;
101 | }
102 |
103 | function DeprecatedValue(_: unknown) {
104 | throw new Error('This command line argument is deprecated.');
105 | }
106 |
--------------------------------------------------------------------------------
/packages/schema-dts-gen/src/cli/cli.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2023 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | import {main} from './internal/main.js';
17 |
18 | function write(content: string) {
19 | process.stdout.write(content, 'utf-8');
20 | }
21 |
22 | main(write)
23 | .then(() => {
24 | process.exit();
25 | })
26 | .catch(e => {
27 | console.error(e);
28 | process.abort();
29 | });
30 |
--------------------------------------------------------------------------------
/packages/schema-dts-gen/src/cli/internal/main.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2023 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import {Store} from 'n3';
18 | import {Log, SetOptions} from '../../logging/index.js';
19 | import {WriteDeclarations} from '../../transform/transform.js';
20 | import {load, loadFile} from '../../triples/reader.js';
21 | import {Context} from '../../ts/context.js';
22 |
23 | import {ParseFlags} from '../args.js';
24 |
25 | export async function main(write: (s: string) => void, args?: string[]) {
26 | const options = ParseFlags(args);
27 | SetOptions(options);
28 |
29 | const ontologyUrl = options.ontology;
30 | const filePath = options.file;
31 | let result: Store;
32 |
33 | if (filePath) {
34 | Log(`Loading Ontology from path: ${filePath}`);
35 | result = await loadFile(filePath);
36 | } else {
37 | Log(`Loading Ontology from URL: ${ontologyUrl}`);
38 | result = await load(ontologyUrl);
39 | }
40 | const context = Context.Parse(options.context);
41 | await WriteDeclarations(result, options.deprecated, context, write);
42 | }
43 |
--------------------------------------------------------------------------------
/packages/schema-dts-gen/src/dep-types/rehype-minify-whitespace.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'rehype-minify-whitespace' {
2 | // eslint-disable-next-line import/no-default-export
3 | export default function minifyWhitespace(): void;
4 | }
5 |
--------------------------------------------------------------------------------
/packages/schema-dts-gen/src/dep-types/remark-wiki-link.d.ts:
--------------------------------------------------------------------------------
1 | declare module 'remark-wiki-link' {
2 | // eslint-disable-next-line import/no-default-export
3 | export default function wikiLinkPlugin(opts?: {
4 | permalinks?: string;
5 | pageResolver?: (l: string) => string[];
6 | hrefTemplate?: (l: string) => string;
7 | wikiLinkClassName?: string;
8 | newClassName?: string;
9 | aliasDivider?: string;
10 | }): void;
11 | }
12 |
--------------------------------------------------------------------------------
/packages/schema-dts-gen/src/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2023 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | // Public Declarations only:
18 | export {WriteDeclarations} from './transform/transform.js';
19 |
20 | export {load as loadTriples} from './triples/reader.js';
21 | export * from './triples/term_utils.js';
22 | export * from './triples/wellKnown.js';
23 |
24 | export * from './ts/class.js';
25 | export * from './ts/enum.js';
26 | export * from './ts/property.js';
27 |
--------------------------------------------------------------------------------
/packages/schema-dts-gen/src/logging/index.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2023 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | let verbose = false;
18 | let logger = console.error;
19 |
20 | export function SetOptions(options: {verbose: boolean}): void {
21 | verbose = options.verbose;
22 | }
23 |
24 | export function Log(message: string): void {
25 | if (verbose) {
26 | logger(message);
27 | }
28 | }
29 |
30 | export function SetLogger(newLogger: (msg: string) => void) {
31 | const previousLogger = logger;
32 | function RestoreLogger() {
33 | logger = previousLogger;
34 | }
35 | logger = newLogger;
36 | return RestoreLogger;
37 | }
38 |
--------------------------------------------------------------------------------
/packages/schema-dts-gen/src/transform/toClass.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2023 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import {Log} from '../logging/index.js';
18 |
19 | import {
20 | IsDirectlyNamedClass,
21 | IsDataType,
22 | ClassIsDataType,
23 | IsSubclass,
24 | TypedTopic,
25 | } from '../triples/wellKnown.js';
26 | import {
27 | AliasBuiltin,
28 | Class,
29 | ClassMap,
30 | DataTypeUnion,
31 | RoleBuiltin,
32 | } from '../ts/class.js';
33 | import {assert, assertIs} from '../util/assert.js';
34 |
35 | import type {Quad} from 'n3';
36 | import {NamedNode} from 'n3';
37 | import {shortStr} from '../index.js';
38 |
39 | function toClass(cls: Class, topic: TypedTopic, map: ClassMap): Class {
40 | const rest: Quad[] = [];
41 |
42 | for (const value of topic.quads) {
43 | const added = cls.add(value, map);
44 | if (!added) rest.push(value);
45 | }
46 |
47 | if (rest.length > 0) {
48 | Log(
49 | `Class ${shortStr(cls.subject)}: Did not add [${rest
50 | .map(r => `(${shortStr(r.predicate)} ${shortStr(r.object)})`)
51 | .join(',')}]`,
52 | );
53 | }
54 | return cls;
55 | }
56 |
57 | function buildAlias(name: string, alias: string): AliasBuiltin[] {
58 | return [
59 | new AliasBuiltin(
60 | new NamedNode(`http://schema.org/${name}`),
61 | AliasBuiltin.Alias(alias),
62 | ),
63 | new AliasBuiltin(
64 | new NamedNode(`https://schema.org/${name}`),
65 | AliasBuiltin.Alias(alias),
66 | ),
67 | ];
68 | }
69 | const wellKnownTypes = [
70 | ...buildAlias('Text', 'string'),
71 | // IMPORTANT: In the future, if possible, we should have: `${number}` in Float only,
72 | // an integer string literal in Integer only, and Number becomes simply Float|Integer.
73 | new AliasBuiltin(
74 | new NamedNode('http://schema.org/Number'),
75 | AliasBuiltin.Alias('number'),
76 | AliasBuiltin.NumberStringLiteral(),
77 | ),
78 | new AliasBuiltin(
79 | new NamedNode('https://schema.org/Number'),
80 | AliasBuiltin.Alias('number'),
81 | AliasBuiltin.NumberStringLiteral(),
82 | ),
83 | ...buildAlias('Time', 'string'),
84 | ...buildAlias('Date', 'string'),
85 | ...buildAlias('DateTime', 'string'),
86 | ...buildAlias('Boolean', 'boolean'),
87 | new RoleBuiltin(new NamedNode('http://schema.org/Role')),
88 | new RoleBuiltin(new NamedNode('http://schema.org/OrganizationRole')),
89 | new RoleBuiltin(new NamedNode('http://schema.org/EmployeeRole')),
90 | new RoleBuiltin(new NamedNode('http://schema.org/LinkRole')),
91 | new RoleBuiltin(new NamedNode('http://schema.org/PerformanceRole')),
92 | new RoleBuiltin(new NamedNode('https://schema.org/Role')),
93 | new RoleBuiltin(new NamedNode('https://schema.org/OrganizationRole')),
94 | new RoleBuiltin(new NamedNode('https://schema.org/EmployeeRole')),
95 | new RoleBuiltin(new NamedNode('https://schema.org/LinkRole')),
96 | new RoleBuiltin(new NamedNode('https://schema.org/PerformanceRole')),
97 | ];
98 |
99 | // Should we allow 'string' to be a valid type for all values of this type?
100 | const wellKnownStrings = [
101 | new NamedNode('http://schema.org/Quantity'),
102 | new NamedNode('http://schema.org/EntryPoint'),
103 | new NamedNode('http://schema.org/Organization'),
104 | new NamedNode('http://schema.org/Person'),
105 | new NamedNode('http://schema.org/Place'),
106 | new NamedNode('https://schema.org/Quantity'),
107 | new NamedNode('https://schema.org/EntryPoint'),
108 | new NamedNode('https://schema.org/Organization'),
109 | new NamedNode('https://schema.org/Person'),
110 | new NamedNode('https://schema.org/Place'),
111 | ];
112 |
113 | function ForwardDeclareClasses(topics: readonly TypedTopic[]): ClassMap {
114 | const classes = new Map();
115 | const dataType = new DataTypeUnion(
116 | new NamedNode('http://schema.org/DataType'),
117 | [],
118 | );
119 |
120 | for (const topic of topics) {
121 | if (IsDataType(topic.subject)) {
122 | classes.set(topic.subject.value, dataType);
123 | continue;
124 | } else if (!IsDirectlyNamedClass(topic) && !IsSubclass(topic)) continue;
125 |
126 | const wk = wellKnownTypes.find(wk => wk.subject.equals(topic.subject));
127 | if (ClassIsDataType(topic)) {
128 | assert(
129 | wk,
130 | `${topic.subject.value} must have corresponding well-known type.`,
131 | );
132 | dataType.wk.push(wk);
133 |
134 | wk['_isDataType'] = true;
135 | }
136 |
137 | assertIs(topic.subject, (s): s is NamedNode => s.termType === 'NamedNode');
138 | const cls = wk || new Class(topic.subject);
139 | const allowString = wellKnownStrings.some(wks => wks.equals(topic.subject));
140 | if (allowString) cls.addTypedef(AliasBuiltin.Alias('string'));
141 | if (IsDirectlyNamedClass(topic)) cls.markAsExplicitClass();
142 |
143 | classes.set(topic.subject.value, cls);
144 | }
145 |
146 | return classes;
147 | }
148 |
149 | function BuildClasses(topics: readonly TypedTopic[], classes: ClassMap) {
150 | for (const topic of topics) {
151 | if (!IsDirectlyNamedClass(topic) && !IsSubclass(topic)) continue;
152 |
153 | const cls = classes.get(topic.subject.value);
154 | assert(cls);
155 | toClass(cls, topic, classes);
156 | }
157 |
158 | for (const cls of classes.values()) {
159 | cls.validateClass();
160 | }
161 | }
162 |
163 | /**
164 | * Produce a mapping of all Classes within the Ontology. The resulting classes
165 | * are empty and only describes their names, comments, and inheritance
166 | * relations.
167 | *
168 | * @param topics a sequence of processed triples describing an Ontology.
169 | * @returns ClassMap Mapping fully qualified ID of each type to a Class.
170 | */
171 | export function ProcessClasses(topics: readonly TypedTopic[]): ClassMap {
172 | const classes = ForwardDeclareClasses(topics);
173 | BuildClasses(topics, classes);
174 | return classes;
175 | }
176 |
--------------------------------------------------------------------------------
/packages/schema-dts-gen/src/transform/toEnum.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2023 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import {NamedNode, Quad} from 'n3';
18 |
19 | import {Log} from '../logging/index.js';
20 | import {shortStr} from '../triples/term_utils.js';
21 | import {HasEnumType, TypedTopic} from '../triples/wellKnown.js';
22 | import {ClassMap} from '../ts/class.js';
23 | import {EnumValue} from '../ts/enum.js';
24 | import {assertIs} from '../util/assert.js';
25 |
26 | /**
27 | * Annotates classes with any Enum values they blong to.
28 | *
29 | * @param topics a sequence of processed triples describing an Ontology.
30 | * @param classes return value of `ProcessClasses`.
31 | */
32 | export function ProcessEnums(topics: readonly TypedTopic[], classes: ClassMap) {
33 | // Process Enums
34 | for (const topic of topics) {
35 | if (!HasEnumType(topic.types)) continue;
36 |
37 | // Everything Here should be an enum.
38 | assertIs(topic.subject, (s): s is NamedNode => s.termType === 'NamedNode');
39 | const enumValue = new EnumValue(topic.subject, topic.types, classes);
40 |
41 | const skipped: Quad[] = [];
42 | for (const v of topic.quads) {
43 | if (!enumValue.add(v)) skipped.push(v);
44 | }
45 |
46 | if (skipped.length > 0) {
47 | Log(
48 | `For Enum Item ${shortStr(topic.subject)}, did not process:\n\t${skipped
49 | .map(q => `(${shortStr(q.predicate)}, ${shortStr(q.object)})`)
50 | .join('\n\t')}`,
51 | );
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/packages/schema-dts-gen/src/transform/toProperty.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2023 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | import {NamedNode, Quad} from 'n3';
17 | import {shortStr} from '../index.js';
18 | import {Log} from '../logging/index.js';
19 |
20 | import {IsPropertyType, TypedTopic} from '../triples/wellKnown.js';
21 | import {ClassMap} from '../ts/class.js';
22 | import {PropertyType} from '../ts/property.js';
23 | import {assertIs} from '../util/assert.js';
24 |
25 | /**
26 | * Annotates classes with any Property values they blong to.
27 | *
28 | * @param topics a sequence of processed triples describing an Ontology.
29 | * @param classes return value of `ProcessClasses`.
30 | */
31 | export function ProcessProperties(
32 | topics: readonly TypedTopic[],
33 | classes: ClassMap,
34 | ) {
35 | for (const topic of topics) {
36 | // Skip Topics that have no 'Property' Type.
37 | if (!topic.types.some(IsPropertyType)) continue;
38 |
39 | const rest: Quad[] = [];
40 | assertIs(topic.subject, (s): s is NamedNode => s.termType === 'NamedNode');
41 | const property = new PropertyType(topic.subject);
42 | for (const value of topic.quads) {
43 | const added = property.add(value, classes);
44 | if (!added) {
45 | rest.push(value);
46 | }
47 | }
48 | // Go over RangeIncludes or DomainIncludes:
49 | if (rest.length > 0) {
50 | Log(
51 | `Still unadded for property: ${shortStr(topic.subject)}:\n\t${rest
52 | .map(q => `(${shortStr(q.predicate)} ${shortStr(q.object)})`)
53 | .join('\n\t')}`,
54 | );
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/packages/schema-dts-gen/src/transform/transform.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2023 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | import ts from 'typescript';
17 | const {
18 | createPrinter,
19 | createSourceFile,
20 | EmitHint,
21 | NewLineKind,
22 | ScriptKind,
23 | ScriptTarget,
24 | } = ts;
25 |
26 | import {asTopicArray} from '../triples/operators.js';
27 | import {Sort} from '../ts/class.js';
28 | import {Context} from '../ts/context.js';
29 |
30 | import {ProcessClasses} from './toClass.js';
31 | import {ProcessEnums} from './toEnum.js';
32 | import {ProcessProperties} from './toProperty.js';
33 | import {HelperTypes} from '../ts/helper_types.js';
34 |
35 | import {Store} from 'n3';
36 |
37 | /**
38 | * Writes TypeScript declarations for all Classes, Typedefs, and Enums
39 | * representing the ontology passed in the 'triples' parameter.
40 | *
41 | * @param triples Observable emitting all Triples found in an ontology.
42 | * @param includeDeprecated True if classes and properties marked with
43 | * 'supersededBy' should still be included (as 'deprecated') in the final
44 | * TypeScript output.
45 | * @param write Callback function to write a portion of the file. Will be called
46 | * sequentially on separate chunks within a file.
47 | *
48 | * @returns Promise indicating completion.
49 | */
50 | export async function WriteDeclarations(
51 | graph: Store,
52 | includeDeprecated: boolean,
53 | context: Context,
54 | write: (content: string) => Promise | void,
55 | ) {
56 | const topics = asTopicArray(graph);
57 |
58 | const classes = ProcessClasses(topics);
59 | ProcessProperties(topics, classes);
60 | ProcessEnums(topics, classes);
61 | const sorted = Array.from(classes.values()).sort(Sort);
62 |
63 | const properties = {
64 | hasRole: !!(
65 | classes.get('http://schema.org/Role') ||
66 | classes.get('https://schema.org/Role')
67 | ),
68 | skipDeprecatedProperties: !includeDeprecated,
69 | };
70 |
71 | const source = createSourceFile(
72 | 'result.ts',
73 | '',
74 | ScriptTarget.ES2015,
75 | /*setParentNodes=*/ false,
76 | ScriptKind.TS,
77 | );
78 | const printer = createPrinter({newLine: NewLineKind.LineFeed});
79 |
80 | for (const helperType of HelperTypes(context, properties)) {
81 | await write(printer.printNode(EmitHint.Unspecified, helperType, source));
82 | await write('\n');
83 | }
84 | await write('\n');
85 |
86 | for (const cls of sorted) {
87 | if (cls.deprecated && !includeDeprecated) continue;
88 |
89 | for (const node of cls.toNode(context, properties)) {
90 | const result = printer.printNode(EmitHint.Unspecified, node, source);
91 | await write(result);
92 | await write('\n');
93 | }
94 | await write('\n');
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/packages/schema-dts-gen/src/triples/operators.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2023 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import {Store} from 'n3';
18 | import {GetTypes, IsType} from './wellKnown.js';
19 | import type {TypedTopic} from './wellKnown.js';
20 |
21 | export function asTopicArray(store: Store): TypedTopic[] {
22 | return store
23 | .getSubjects(null, null, null)
24 | .map(subject => ({
25 | subject,
26 | quads: store.getQuads(subject, null, null, null),
27 | }))
28 | .map(topic => ({
29 | subject: topic.subject,
30 | quads: topic.quads.filter(value => !IsType(value.predicate)),
31 | types: GetTypes(topic.quads),
32 | }));
33 | }
34 |
--------------------------------------------------------------------------------
/packages/schema-dts-gen/src/triples/reader.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2023 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | import https from 'https';
17 | import fs from 'fs/promises';
18 |
19 | import {Log} from '../logging/index.js';
20 |
21 | import {Parser, Store} from 'n3';
22 | import type {Quad} from 'n3';
23 |
24 | function asQuads(data: string): Quad[] {
25 | return new Parser({}).parse(data);
26 | }
27 |
28 | /**
29 | * Loads schema all Triples from a given Schema file and version.
30 | */
31 | export async function load(url: string): Promise {
32 | const quads = await handleUrl(url);
33 | return process(quads);
34 | }
35 |
36 | /**
37 | * does the same as load(), but for a local file
38 | */
39 | export async function loadFile(path: string): Promise {
40 | const quads = await handleFile(path);
41 | return process(quads);
42 | }
43 |
44 | async function handleFile(path: string): Promise {
45 | const fileStr = await fs.readFile(path, {encoding: 'utf8'});
46 | return asQuads(fileStr);
47 | }
48 |
49 | function handleUrl(url: string): Promise {
50 | return new Promise((resolve, reject) => {
51 | https
52 | .get(url, response => {
53 | Log(`Got Response ${response.statusCode}: ${response.statusMessage}.`);
54 | if (response.statusCode !== 200) {
55 | const location =
56 | response.headers['location'] ||
57 | response.headers['content-location'];
58 |
59 | if (location) {
60 | Log(`Handling redirect to ${location}...`);
61 | resolve(handleUrl(location));
62 | return;
63 | }
64 |
65 | reject(
66 | new Error(
67 | `Got Errored Response ${response.statusCode}: ${response.statusMessage}.`,
68 | ),
69 | );
70 | return;
71 | }
72 |
73 | const data: string[] = [];
74 |
75 | response.on('data', (chunkB: Buffer) => {
76 | const chunk = chunkB.toString('utf-8');
77 | data.push(chunk);
78 | });
79 |
80 | response.on('end', () => {
81 | try {
82 | resolve(asQuads(data.join('')));
83 | } catch (error) {
84 | Log(`Caught Error on end: ${error as Error}`);
85 | reject(error as Error);
86 | }
87 | });
88 |
89 | response.on('error', error => {
90 | Log(`Saw error: ${error}`);
91 | reject(error);
92 | });
93 | })
94 | .on('error', reject);
95 | });
96 | }
97 |
98 | export function process(quads: Quad[]): Store {
99 | const filtered = quads.filter(quad => {
100 | if (
101 | quad.subject.termType === 'NamedNode' &&
102 | quad.subject.value.includes('file:///')
103 | ) {
104 | // Inexplicably, local files end up in the public schema for
105 | // certain layer overlays.
106 | return false;
107 | }
108 |
109 | return true;
110 | });
111 | return new Store(filtered);
112 | }
113 |
--------------------------------------------------------------------------------
/packages/schema-dts-gen/src/triples/term_utils.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2023 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import type {NamedNode, Term} from 'n3';
18 |
19 | export function nameFromContext(
20 | term: NamedNode,
21 | context: string,
22 | ): string | null {
23 | if (term.id.startsWith(context)) {
24 | return cleanName(term.id.replace(new RegExp(`^${context}`), ''));
25 | }
26 |
27 | if (context.startsWith('https:')) {
28 | return nameFromContext(term, context.replace(/^https:/, 'http:'));
29 | }
30 |
31 | return null;
32 | }
33 | function cleanName(n: string | null): string | null {
34 | if (!n) return n;
35 | return n.replace(/^[#/]/, '');
36 | }
37 |
38 | export function namedPortionOrEmpty(term: NamedNode): string {
39 | const url = new URL(term.id);
40 | if (url.hash.startsWith('#')) return url.hash.replace(/^#/, '');
41 | const path = url.pathname.split('/');
42 | return path[path.length - 1];
43 | }
44 |
45 | export function namedPortion(term: NamedNode): string {
46 | const name = namedPortionOrEmpty(term);
47 |
48 | if (!name) {
49 | throw new Error(
50 | `Expected ${term.id} to have a short name (final path or hash), but found none.`,
51 | );
52 | }
53 | return name;
54 | }
55 |
56 | export function shortStr(term: Term) {
57 | if (term.termType === 'NamedNode') {
58 | return namedPortionOrEmpty(term) || term.value;
59 | }
60 | return term.value;
61 | }
62 |
--------------------------------------------------------------------------------
/packages/schema-dts-gen/src/ts/action_constraints.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2023 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import ts, {factory, ModifierFlags, SyntaxKind} from 'typescript';
18 | import {arrayOf} from './util/arrayof.js';
19 | import {withComments} from './util/comments.js';
20 |
21 | const ActionBaseName = 'ActionBase';
22 | const InputActionConstraintsName = 'InputActionConstraints';
23 | const OutputActionConstraintsName = 'OutputActionConstraints';
24 |
25 | function createIOActionConstraints(
26 | suffix: 'input' | 'output',
27 | ): ts.TypeAliasDeclaration {
28 | const name =
29 | suffix === 'input'
30 | ? InputActionConstraintsName
31 | : OutputActionConstraintsName;
32 | return factory.createTypeAliasDeclaration(
33 | undefined,
34 | name,
35 | arrayOf(
36 | factory.createTypeParameterDeclaration(
37 | [],
38 | 'T',
39 | factory.createTypeReferenceNode(ActionBaseName),
40 | ),
41 | ),
42 | factory.createTypeReferenceNode('Partial', [
43 | factory.createMappedTypeNode(
44 | undefined,
45 | factory.createTypeParameterDeclaration(
46 | undefined,
47 | 'K',
48 | factory.createTypeReferenceNode('Exclude', [
49 | factory.createTypeOperatorNode(
50 | SyntaxKind.KeyOfKeyword,
51 | factory.createTypeReferenceNode('T'),
52 | ),
53 | factory.createTemplateLiteralType(factory.createTemplateHead('@'), [
54 | factory.createTemplateLiteralTypeSpan(
55 | factory.createKeywordTypeNode(SyntaxKind.StringKeyword),
56 | factory.createTemplateTail(''),
57 | ),
58 | ]),
59 | ]),
60 | ),
61 | factory.createTemplateLiteralType(factory.createTemplateHead(''), [
62 | factory.createTemplateLiteralTypeSpan(
63 | factory.createIntersectionTypeNode([
64 | factory.createKeywordTypeNode(SyntaxKind.StringKeyword),
65 | factory.createTypeReferenceNode('K'),
66 | ]),
67 | factory.createTemplateTail(`-${suffix}`),
68 | ),
69 | ]),
70 | undefined,
71 | factory.createUnionTypeNode([
72 | factory.createTypeReferenceNode('PropertyValueSpecification'),
73 | factory.createKeywordTypeNode(SyntaxKind.StringKeyword),
74 | ]),
75 | undefined,
76 | ),
77 | ]),
78 | );
79 | }
80 |
81 | export const InputActionConstraintsType = createIOActionConstraints('input');
82 | export const OutputActionConstraintsType = createIOActionConstraints('output');
83 | export const WithActionConstraintsType = withComments(
84 | 'Provides input and output action constraints for an action.',
85 | factory.createTypeAliasDeclaration(
86 | factory.createModifiersFromModifierFlags(ModifierFlags.Export),
87 | 'WithActionConstraints',
88 | arrayOf(
89 | factory.createTypeParameterDeclaration(
90 | [],
91 | 'T',
92 | factory.createTypeReferenceNode(ActionBaseName),
93 | ),
94 | ),
95 | factory.createIntersectionTypeNode([
96 | factory.createTypeReferenceNode('T'),
97 | factory.createTypeReferenceNode(InputActionConstraintsName, [
98 | factory.createTypeReferenceNode('T'),
99 | ]),
100 | factory.createTypeReferenceNode(OutputActionConstraintsName, [
101 | factory.createTypeReferenceNode('T'),
102 | ]),
103 | ]),
104 | ),
105 | );
106 |
--------------------------------------------------------------------------------
/packages/schema-dts-gen/src/ts/context.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2023 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import ts from 'typescript';
18 | import type {PropertySignature} from 'typescript';
19 | const {factory} = ts;
20 |
21 | import {NamedNode} from 'n3';
22 | import {nameFromContext} from '../triples/term_utils.js';
23 |
24 | export class Context {
25 | private readonly context: Array = [];
26 |
27 | setUrlContext(ctx: string) {
28 | if (this.context.length > 0) {
29 | throw new Error(
30 | 'Attempting to set a default URL context, ' +
31 | 'but other named contexts exist already.',
32 | );
33 | }
34 | this.context.push(['', ctx]);
35 | }
36 | addNamedContext(name: string, url: string) {
37 | this.context.push([name, url]);
38 | }
39 | validate() {
40 | if (this.context.length === 0) throw new Error('Invalid empty context.');
41 | if (this.context.length === 1) return; // One context is always right.
42 |
43 | // Make sure no key is duplicated.
44 | const seen: Set = new Set();
45 | for (const [name] of this.context) {
46 | if (seen.has(name)) {
47 | throw new Error(`Named context ${name} found twice in context.`);
48 | }
49 | if (name === '') {
50 | throw new Error(
51 | 'Context with multiple named contexts includes unnamed URL.',
52 | );
53 | }
54 | seen.add(name);
55 | }
56 | }
57 |
58 | getScopedName(node: NamedNode): string {
59 | for (const [ctxName, url] of this.context) {
60 | const name = nameFromContext(node, url);
61 | if (name !== null) {
62 | // Valid possibilities:
63 | // - "schema:Foo" when name == schema && node.name == Foo.
64 | // - "schema:" when name == schema && node.name is undefined.
65 | // - "Foo" when name is empty and node.name is Foo.
66 | //
67 | // Don't allow "" when name is empty and node.name is undefined.
68 | return ctxName === '' ? name || node.id : `${ctxName}:${name}`;
69 | }
70 | }
71 | return node.id;
72 | }
73 |
74 | private typeNode() {
75 | // Either:
76 | // "@context": "https://schema.org" or similar:
77 | if (this.context.length === 1 && this.context[0][0] === '') {
78 | return factory.createLiteralTypeNode(
79 | factory.createStringLiteral(this.context[0][1]),
80 | );
81 | }
82 |
83 | // or "@context" is a literal object type of key-values of URLs:
84 | return factory.createTypeLiteralNode(
85 | /*members=*/ this.context.map(([name, url]) =>
86 | factory.createPropertySignature(
87 | /*modifiers=*/ [],
88 | factory.createStringLiteral(name),
89 | /*questionToken=*/ undefined,
90 | factory.createLiteralTypeNode(factory.createStringLiteral(url)),
91 | ),
92 | ),
93 | );
94 | }
95 |
96 | /** Creates the full property signature for "@context" given the configured context. */
97 | contextProperty(): PropertySignature {
98 | return factory.createPropertySignature(
99 | /*modifiers=*/ [],
100 | factory.createStringLiteral('@context'),
101 | /*questionToken=*/ undefined,
102 | this.typeNode(),
103 | );
104 | }
105 |
106 | static Parse(contextSpec: string): Context {
107 | const keyVals = contextSpec
108 | .split(',')
109 | .map(s => s.trim())
110 | .filter(s => !!s);
111 | const context = new Context();
112 |
113 | if (keyVals.length === 1) {
114 | context.setUrlContext(keyVals[0]);
115 | } else {
116 | for (const keyVal of keyVals) {
117 | const match = /^([^:]+):((http|https):.+)$/g.exec(keyVal);
118 | if (!match || match[1] === undefined || match[2] === undefined) {
119 | throw new Error(`Unknown value ${keyVal} in --context flag.`);
120 | }
121 | context.addNamedContext(match[1], match[2]);
122 | }
123 | }
124 |
125 | context.validate();
126 | return context;
127 | }
128 | }
129 |
--------------------------------------------------------------------------------
/packages/schema-dts-gen/src/ts/enum.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2023 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 | import ts from 'typescript';
17 | import type {TypeNode} from 'typescript';
18 | const {factory} = ts;
19 |
20 | import {Log} from '../logging/index.js';
21 |
22 | import {GetComment, IsClassType, IsDataType} from '../triples/wellKnown.js';
23 |
24 | import {ClassMap} from './class.js';
25 | import {Context} from './context.js';
26 |
27 | import type {NamedNode, Quad} from 'n3';
28 |
29 | /**
30 | * Corresponds to a value that belongs to an Enumeration.
31 | */
32 | export class EnumValue {
33 | readonly INSTANCE = 'EnumValue';
34 |
35 | private comment?: string;
36 | constructor(
37 | readonly value: NamedNode,
38 | types: readonly NamedNode[],
39 | map: ClassMap,
40 | ) {
41 | for (const type of types) {
42 | // If a Subject has a "Type", then it either means:
43 | // 1- Type is Class - This topic represents an object that can be
44 | // represented as a class (usually, a node/object).
45 | // 2- Type is DataType - This topic represents an object that can
46 | // represented as a raw value.
47 | // 3- Type is Neither - This topic's IRI can be used in the place of that
48 | // type to describe its value.
49 | //
50 | // For example,
51 | // - Thing is a Class only.
52 | // - Text is a Class and a DataType.
53 | // - DataType is a Class.
54 | // - Wednesday is a DayOfWeek only.
55 | //
56 | // In Schema.org 3.4, some enumerations were both a Class and an Enum.
57 | //
58 | // For example, SurgicalProcedure was both an enum value for
59 | // MedicalProcedureType and a class that can be described in its own
60 | // right. It had type Class and MedicalProcedureType.
61 | //
62 | // For those cases, we make sure:
63 | // (a) We add an EnumValue for all types that are not Class/DataType.
64 | // (b) An EnumValue being a Class/DataType should not disqualify it from
65 | // being an enum value for some other type (if it has one).
66 | if (IsClassType(type) || IsDataType(type)) continue;
67 |
68 | const enumObject = map.get(type.id);
69 | if (!enumObject) {
70 | throw new Error(`Couldn't find ${type.id} in classes.`);
71 | }
72 | enumObject.addEnum(this);
73 | }
74 | }
75 |
76 | add(value: Quad) {
77 | const comment = GetComment(value);
78 | if (comment) {
79 | if (this.comment) {
80 | Log(
81 | `Duplicate comments provided on ${this.value.id} enum but one already exists. It will be overwritten.`,
82 | );
83 | }
84 | this.comment = comment.comment;
85 | return true;
86 | }
87 |
88 | return false;
89 | }
90 |
91 | toTypeLiteral(context: Context): TypeNode[] {
92 | const types = [this.value.id];
93 |
94 | if (this.value.id.startsWith('http:')) {
95 | types.push(this.value.id.replace(/^http:/, 'https:'));
96 | }
97 |
98 | const scoped = context.getScopedName(this.value);
99 | if (scoped !== this.value.id) {
100 | types.push(scoped);
101 | }
102 |
103 | return types.map(t =>
104 | factory.createLiteralTypeNode(factory.createStringLiteral(t)),
105 | );
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/packages/schema-dts-gen/src/ts/helper_types.ts:
--------------------------------------------------------------------------------
1 | import ts from 'typescript';
2 | import type {TypeNode} from 'typescript';
3 | const {factory, SyntaxKind, ModifierFlags} = ts;
4 |
5 | import {
6 | WithActionConstraintsType,
7 | InputActionConstraintsType,
8 | OutputActionConstraintsType,
9 | } from './action_constraints.js';
10 | import {Context} from './context.js';
11 |
12 | import {arrayOf} from './util/arrayof.js';
13 | import {withComments} from './util/comments.js';
14 | import {typeUnion} from './util/union.js';
15 |
16 | function IdPropertyNode() {
17 | return withComments(
18 | 'IRI identifying the canonical address of this object.',
19 | factory.createPropertySignature(
20 | /* modifiers= */ [],
21 | factory.createStringLiteral('@id'),
22 | /* questionToken= */ undefined,
23 | /* typeNode= */
24 | factory.createTypeReferenceNode('string', /*typeArguments=*/ []),
25 | ),
26 | );
27 | }
28 |
29 | function WithContextType(context: Context) {
30 | // export type WithContext = T & { "@context": TYPE_NODE }
31 | return withComments(
32 | 'Used at the top-level node to indicate the context for the JSON-LD ' +
33 | 'objects used. The context provided in this type is compatible ' +
34 | 'with the keys and URLs in the rest of this generated file.',
35 | factory.createTypeAliasDeclaration(
36 | factory.createModifiersFromModifierFlags(ModifierFlags.Export),
37 | 'WithContext',
38 | [
39 | factory.createTypeParameterDeclaration(
40 | /*modifiers=*/ [],
41 | 'T' /*constraint=*/,
42 | factory.createTypeReferenceNode(
43 | 'Thing',
44 | /*typeArguments=*/ undefined,
45 | ),
46 | ),
47 | ],
48 | factory.createIntersectionTypeNode([
49 | factory.createTypeReferenceNode('T', /*typeArguments=*/ undefined),
50 | factory.createTypeLiteralNode([context.contextProperty()]),
51 | ]),
52 | ),
53 | );
54 | }
55 |
56 | function GraphType(context: Context) {
57 | return withComments(
58 | '',
59 | factory.createInterfaceDeclaration(
60 | factory.createModifiersFromModifierFlags(ModifierFlags.Export),
61 | GraphTypeName,
62 | /*typeParameters=*/ undefined,
63 | /*heritageClauses=*/ undefined,
64 | [
65 | context.contextProperty(),
66 | factory.createPropertySignature(
67 | /*modifiers=*/ [],
68 | factory.createStringLiteral('@graph'),
69 | /*questionToken=*/ undefined,
70 | factory.createTypeOperatorNode(
71 | SyntaxKind.ReadonlyKeyword,
72 | factory.createArrayTypeNode(
73 | factory.createTypeReferenceNode(
74 | 'Thing',
75 | /*typeArguments=*/ undefined,
76 | ),
77 | ),
78 | ),
79 | ),
80 | ],
81 | ),
82 | );
83 | }
84 |
85 | const SchemaValueName = 'SchemaValue';
86 | export const IdReferenceName = 'IdReference';
87 | export const GraphTypeName = 'Graph';
88 |
89 | export function SchemaValueReference(
90 | {hasRole}: {hasRole: boolean},
91 | makeScalarType: () => TypeNode,
92 | propertyName: string,
93 | ) {
94 | return factory.createTypeReferenceNode(
95 | SchemaValueName,
96 | /* typeArguments = */ arrayOf(
97 | makeScalarType(),
98 | hasRole &&
99 | factory.createLiteralTypeNode(
100 | factory.createStringLiteral(propertyName),
101 | ),
102 | ),
103 | );
104 | }
105 |
106 | export function HelperTypes(context: Context, {hasRole}: {hasRole: boolean}) {
107 | return [
108 | WithContextType(context),
109 | GraphType(context),
110 | factory.createTypeAliasDeclaration(
111 | /*modifiers=*/ [],
112 | SchemaValueName,
113 | arrayOf(
114 | factory.createTypeParameterDeclaration(/*modifiers=*/ [], 'T'),
115 | hasRole &&
116 | factory.createTypeParameterDeclaration(
117 | /*modifiers=*/ [],
118 | 'TProperty',
119 | /*constraint=*/ factory.createTypeReferenceNode('string'),
120 | ),
121 | ),
122 | factory.createUnionTypeNode(
123 | arrayOf(
124 | factory.createTypeReferenceNode('T', /*typeArguments=*/ []),
125 | hasRole &&
126 | factory.createTypeReferenceNode('Role', [
127 | /*TContent=*/ factory.createTypeReferenceNode('T'),
128 | /*TProperty=*/ factory.createTypeReferenceNode('TProperty'),
129 | ]),
130 | factory.createTypeOperatorNode(
131 | SyntaxKind.ReadonlyKeyword,
132 | factory.createArrayTypeNode(
133 | typeUnion(
134 | factory.createTypeReferenceNode('T', /*typeArguments=*/ []),
135 | hasRole &&
136 | factory.createTypeReferenceNode('Role', [
137 | /*TContent=*/ factory.createTypeReferenceNode('T'),
138 | /*TProperty=*/ factory.createTypeReferenceNode('TProperty'),
139 | ]),
140 | ),
141 | ),
142 | ),
143 | ),
144 | ),
145 | ),
146 | factory.createTypeAliasDeclaration(
147 | /*modifiers=*/ [],
148 | IdReferenceName,
149 | /*typeParameters=*/ [],
150 | factory.createTypeLiteralNode([IdPropertyNode()]),
151 | ),
152 | InputActionConstraintsType,
153 | OutputActionConstraintsType,
154 | WithActionConstraintsType,
155 | ];
156 | }
157 |
--------------------------------------------------------------------------------
/packages/schema-dts-gen/src/ts/property.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2023 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import ts from 'typescript';
18 | import type {PropertySignature} from 'typescript';
19 | const {factory, SyntaxKind} = ts;
20 |
21 | import {Log} from '../logging/index.js';
22 | import {
23 | GetComment,
24 | IsDomainIncludes,
25 | IsRangeIncludes,
26 | IsSupersededBy,
27 | IsTypeName,
28 | } from '../triples/wellKnown.js';
29 |
30 | import type {Class, ClassMap} from './class.js';
31 | import {Context} from './context.js';
32 | import {appendParagraph, withComments} from './util/comments.js';
33 | import {IdReferenceName, SchemaValueReference} from './helper_types.js';
34 | import {typeUnion} from './util/union.js';
35 |
36 | import type {NamedNode, Quad} from 'n3';
37 | import {assertIs} from '../util/assert.js';
38 |
39 | /**
40 | * A "class" of properties, not associated with any particuar object.
41 | */
42 | export class PropertyType {
43 | private readonly types: Class[] = [];
44 | private _comment?: string;
45 | private readonly _supersededBy: NamedNode[] = [];
46 |
47 | constructor(private readonly subject: NamedNode) {}
48 |
49 | get comment() {
50 | if (!this.deprecated) return this._comment;
51 | const deprecated = `@deprecated Consider using ${this._supersededBy
52 | .map(o => o.id)
53 | .join(' or ')} instead.`;
54 |
55 | return appendParagraph(this._comment, deprecated);
56 | }
57 |
58 | get deprecated() {
59 | return this._supersededBy.length > 0;
60 | }
61 |
62 | add(value: Quad, classes: ClassMap): boolean {
63 | const c = GetComment(value);
64 | if (c) {
65 | if (this._comment) {
66 | Log(
67 | `Duplicate comments provided on property ${this.subject.id}. It will be overwritten.`,
68 | );
69 | }
70 | this._comment = c.comment;
71 | return true;
72 | }
73 |
74 | if (IsRangeIncludes(value.predicate)) {
75 | if (!IsTypeName(value.object)) {
76 | throw new Error(
77 | `Type expected to be a UrlNode always. When adding ${JSON.stringify(
78 | value.toJSON(),
79 | undefined,
80 | 2,
81 | )}.`,
82 | );
83 | }
84 | const cls = classes.get(value.object.id);
85 | if (!cls) {
86 | throw new Error(
87 | `Could not find class for ${value.object.id} [only foud: ${Array.from(
88 | classes.keys(),
89 | ).join(', ')}]`,
90 | );
91 | }
92 | this.types.push(cls);
93 | return true;
94 | }
95 |
96 | if (IsDomainIncludes(value.predicate)) {
97 | const cls = classes.get(value.object.id);
98 | if (!cls) {
99 | throw new Error(
100 | `Could not find class for ${
101 | value.object.id
102 | }. [only foud: ${Array.from(classes.keys()).join(', ')}]`,
103 | );
104 | }
105 | cls.addProp(new Property(this.subject, this));
106 | return true;
107 | }
108 |
109 | if (IsSupersededBy(value.predicate)) {
110 | assertIs(value.object, (o): o is NamedNode => o.termType === 'NamedNode');
111 | this._supersededBy.push(value.object);
112 | return true;
113 | }
114 |
115 | return false;
116 | }
117 |
118 | scalarTypeNode() {
119 | const typeNames = this.types.map(cls => cls.className()).sort();
120 | if (this.types.some(cls => cls.isNodeType())) {
121 | typeNames.push(IdReferenceName);
122 | }
123 |
124 | const typeNodes = typeNames.map(type =>
125 | factory.createTypeReferenceNode(type, /*typeArguments=*/ []),
126 | );
127 |
128 | return typeUnion(...typeNodes);
129 | }
130 | }
131 |
132 | /**
133 | * A Property on a particular object.
134 | */
135 | export class Property {
136 | constructor(
137 | readonly key: NamedNode,
138 | private readonly type: PropertyType,
139 | ) {}
140 |
141 | get deprecated() {
142 | return this.type.deprecated;
143 | }
144 |
145 | private typeNode(context: Context, properties: {hasRole: boolean}) {
146 | return SchemaValueReference(
147 | properties,
148 | () => this.type.scalarTypeNode(),
149 | context.getScopedName(this.key),
150 | );
151 | }
152 |
153 | toNode(context: Context, properties: {hasRole: boolean}): PropertySignature {
154 | return withComments(
155 | this.type.comment,
156 | factory.createPropertySignature(
157 | /* modifiers= */ [],
158 | factory.createStringLiteral(context.getScopedName(this.key)),
159 | factory.createToken(SyntaxKind.QuestionToken),
160 | /*typeNode=*/ this.typeNode(context, properties),
161 | ),
162 | );
163 | }
164 | }
165 |
166 | export class TypeProperty {
167 | constructor(private readonly className: NamedNode) {}
168 |
169 | toNode(context: Context) {
170 | return factory.createPropertySignature(
171 | /* modifiers= */ [],
172 | factory.createStringLiteral('@type'),
173 | /* questionToken= */ undefined,
174 | /* typeNode= */
175 | factory.createTypeReferenceNode(
176 | `"${context.getScopedName(this.className)}"`,
177 | /*typeArguments=*/ undefined,
178 | ),
179 | );
180 | }
181 |
182 | readonly deprecated = false;
183 | }
184 |
--------------------------------------------------------------------------------
/packages/schema-dts-gen/src/ts/util/arrayof.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2023 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | export function arrayOf(
18 | ...args: Array
19 | ): T[] {
20 | return args.filter((elem): elem is T => !!elem);
21 | }
22 |
--------------------------------------------------------------------------------
/packages/schema-dts-gen/src/ts/util/names.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2023 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import type {NamedNode} from 'n3';
18 | import {namedPortion} from '../../triples/term_utils.js';
19 |
20 | function decodeOr(component: string) {
21 | try {
22 | return decodeURIComponent(component);
23 | } catch {
24 | return component;
25 | }
26 | }
27 |
28 | export function toClassName(subject: NamedNode): string {
29 | let sanitizedName = decodeOr(namedPortion(subject)).replace(
30 | /[^A-Za-z0-9_]/g,
31 | '_',
32 | );
33 |
34 | // No leading numbers.
35 | if (/^[0-9]/g.test(sanitizedName)) {
36 | sanitizedName = `_${sanitizedName}`;
37 | }
38 |
39 | return sanitizedName;
40 | }
41 |
--------------------------------------------------------------------------------
/packages/schema-dts-gen/src/ts/util/union.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2023 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import ts from 'typescript';
18 | import type {TypeNode} from 'typescript';
19 | const {factory, SyntaxKind} = ts;
20 |
21 | export function typeUnion(
22 | ...args: Array
23 | ): TypeNode {
24 | const types = args.filter((elem): elem is TypeNode => !!elem);
25 |
26 | switch (types.length) {
27 | case 0:
28 | return factory.createKeywordTypeNode(SyntaxKind.NeverKeyword);
29 | case 1:
30 | return types[0];
31 | default:
32 | return factory.createUnionTypeNode(types);
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/packages/schema-dts-gen/src/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../../../tsconfig-base.json",
3 | "compilerOptions": {
4 | "outDir": "../dist/src",
5 | "rootDir": ".",
6 | "skipLibCheck": true
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/packages/schema-dts-gen/src/util/assert.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2023 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | import {ok} from 'assert';
18 |
19 | export function assert(
20 | item: T | null | undefined,
21 | message?: string | Error,
22 | ): asserts item is T {
23 | ok(item, message);
24 | }
25 |
26 | export function assertIs(
27 | item: T,
28 | assertion: (i: T) => i is U,
29 | message?: string,
30 | ): asserts item is U {
31 | ok(assertion(item), message);
32 | }
33 |
--------------------------------------------------------------------------------
/packages/schema-dts-gen/test/baselines/category_test.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2023 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * @fileoverview Baseline tests are a set of tests (in tests/baseline/) that
17 | * correspond to full comparisons of a generate .ts output based on a set of
18 | * Triples representing an entire ontology.
19 | */
20 | import {basename} from 'path';
21 |
22 | import {inlineCli} from '../helpers/main_driver.js';
23 |
24 | test(`baseline_${basename(import.meta.url)}`, async () => {
25 | const {actual, actualLogs} = await inlineCli(
26 | `
27 | .
28 | .
29 | .
30 | .
31 | .
32 | .
33 | .
34 | "issue-743" .
35 | "Distillery" .
36 | "A distillery." .
37 | .
38 | .
39 | `,
40 | [
41 | '--ontology',
42 | `https://fake.com/${basename(import.meta.url)}.nt`,
43 | '--verbose',
44 | ],
45 | );
46 |
47 | expect(actual).toMatchInlineSnapshot(`
48 | "/** Used at the top-level node to indicate the context for the JSON-LD objects used. The context provided in this type is compatible with the keys and URLs in the rest of this generated file. */
49 | export type WithContext = T & {
50 | "@context": "https://schema.org";
51 | };
52 | export interface Graph {
53 | "@context": "https://schema.org";
54 | "@graph": readonly Thing[];
55 | }
56 | type SchemaValue = T | readonly T[];
57 | type IdReference = {
58 | /** IRI identifying the canonical address of this object. */
59 | "@id": string;
60 | };
61 | type InputActionConstraints = Partial<{
62 | [K in Exclude as \`\${string & K}-input\`]: PropertyValueSpecification | string;
63 | }>;
64 | type OutputActionConstraints = Partial<{
65 | [K in Exclude as \`\${string & K}-output\`]: PropertyValueSpecification | string;
66 | }>;
67 | /** Provides input and output action constraints for an action. */
68 | export type WithActionConstraints = T & InputActionConstraints & OutputActionConstraints;
69 |
70 | export type Text = string;
71 |
72 | interface DistilleryLeaf extends ThingBase {
73 | "@type": "Distillery";
74 | }
75 | /** A distillery. */
76 | export type Distillery = DistilleryLeaf;
77 |
78 | interface ThingBase extends Partial {
79 | "name"?: SchemaValue;
80 | }
81 | interface ThingLeaf extends ThingBase {
82 | "@type": "Thing";
83 | }
84 | export type Thing = ThingLeaf | Distillery;
85 |
86 | "
87 | `);
88 | expect(actualLogs).toMatchInlineSnapshot(`
89 | "Loading Ontology from URL: https://fake.com/category_test.ts.nt
90 | Got Response 200: Ok.
91 | Class Distillery: Did not add [(source 743),(category issue-743),(label Distillery)]
92 | "
93 | `);
94 | });
95 |
--------------------------------------------------------------------------------
/packages/schema-dts-gen/test/baselines/data_type_union_test.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2023 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * @fileoverview Baseline tests are a set of tests (in tests/baseline/) that
17 | * correspond to full comparisons of a generate .ts output based on a set of
18 | * Triples representing an entire ontology.
19 | */
20 | import {basename} from 'path';
21 |
22 | import {inlineCli} from '../helpers/main_driver.js';
23 |
24 | test(`baseline_${basename(import.meta.url)}`, async () => {
25 | const {actual} = await inlineCli(
26 | `
27 | .
28 | .
29 | .
30 | .
31 | .
32 | .
33 | .
34 | .
35 | .
36 | .
37 | .
38 | "The basic data types such as Integers, Strings, etc." .
39 | .
40 | .
41 | `,
42 | ['--ontology', `https://fake.com/${basename(import.meta.url)}.nt`],
43 | );
44 |
45 | expect(actual).toMatchInlineSnapshot(`
46 | "/** Used at the top-level node to indicate the context for the JSON-LD objects used. The context provided in this type is compatible with the keys and URLs in the rest of this generated file. */
47 | export type WithContext = T & {
48 | "@context": "https://schema.org";
49 | };
50 | export interface Graph {
51 | "@context": "https://schema.org";
52 | "@graph": readonly Thing[];
53 | }
54 | type SchemaValue = T | readonly T[];
55 | type IdReference = {
56 | /** IRI identifying the canonical address of this object. */
57 | "@id": string;
58 | };
59 | type InputActionConstraints = Partial<{
60 | [K in Exclude as \`\${string & K}-input\`]: PropertyValueSpecification | string;
61 | }>;
62 | type OutputActionConstraints = Partial<{
63 | [K in Exclude as \`\${string & K}-output\`]: PropertyValueSpecification | string;
64 | }>;
65 | /** Provides input and output action constraints for an action. */
66 | export type WithActionConstraints = T & InputActionConstraints & OutputActionConstraints;
67 |
68 | export type Number = number | \`\${number}\`;
69 |
70 | export type Text = string;
71 |
72 | /** The basic data types such as Integers, Strings, etc. */
73 | export type DataType = Number | Text;
74 |
75 | interface ThingBase extends Partial {
76 | "age"?: SchemaValue;
77 | "name"?: SchemaValue;
78 | }
79 | interface ThingLeaf extends ThingBase {
80 | "@type": "Thing";
81 | }
82 | export type Thing = ThingLeaf;
83 |
84 | "
85 | `);
86 | });
87 |
--------------------------------------------------------------------------------
/packages/schema-dts-gen/test/baselines/default_ontology_test.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2023 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * @fileoverview Baseline tests are a set of tests (in tests/baseline/) that
17 | * correspond to full comparisons of a generate .ts output based on a set of
18 | * Triples representing an entire ontology.
19 | */
20 | import {basename} from 'path';
21 |
22 | import {inlineCli} from '../helpers/main_driver.js';
23 |
24 | test(`baseline_${basename(import.meta.url)}`, async () => {
25 | const {actual, actualLogs} = await inlineCli(
26 | `
27 | .
28 | `,
29 | ['--verbose'],
30 | );
31 |
32 | expect(actual).toMatchInlineSnapshot(`
33 | "/** Used at the top-level node to indicate the context for the JSON-LD objects used. The context provided in this type is compatible with the keys and URLs in the rest of this generated file. */
34 | export type WithContext = T & {
35 | "@context": "https://schema.org";
36 | };
37 | export interface Graph {
38 | "@context": "https://schema.org";
39 | "@graph": readonly Thing[];
40 | }
41 | type SchemaValue = T | readonly T[];
42 | type IdReference = {
43 | /** IRI identifying the canonical address of this object. */
44 | "@id": string;
45 | };
46 | type InputActionConstraints = Partial<{
47 | [K in Exclude as \`\${string & K}-input\`]: PropertyValueSpecification | string;
48 | }>;
49 | type OutputActionConstraints = Partial<{
50 | [K in Exclude as \`\${string & K}-output\`]: PropertyValueSpecification | string;
51 | }>;
52 | /** Provides input and output action constraints for an action. */
53 | export type WithActionConstraints = T & InputActionConstraints & OutputActionConstraints;
54 |
55 | interface ThingBase extends Partial {
56 | }
57 | interface ThingLeaf extends ThingBase {
58 | "@type": "Thing";
59 | }
60 | export type Thing = ThingLeaf;
61 |
62 | "
63 | `);
64 | expect(actualLogs).toMatchInlineSnapshot(`
65 | "Loading Ontology from URL: https://schema.org/version/latest/schemaorg-all-https.nt
66 | Got Response 200: Ok.
67 | "
68 | `);
69 | });
70 |
--------------------------------------------------------------------------------
/packages/schema-dts-gen/test/baselines/duplicate_comments_test.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2023 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * @fileoverview Baseline tests are a set of tests (in tests/baseline/) that
17 | * correspond to full comparisons of a generate .ts output based on a set of
18 | * Triples representing an entire ontology.
19 | */
20 | import {basename} from 'path';
21 |
22 | import {inlineCli} from '../helpers/main_driver.js';
23 |
24 | test(`baseline_${basename(import.meta.url)}`, async () => {
25 | const {actual, actualLogs} = await inlineCli(
26 | `
27 | .
28 | .
29 | .
30 | .
31 | "Simple!" .
32 | "Simple!" .
33 | "Names are great!\\n
Y"@en .
34 | "Things are amazing!\\n\\n
- Foo
- Bar
- Baz, and Bat
"@en .
35 | .
36 | .
37 | "Simple!" .
38 | "Complex!" .
39 | .
40 | .
41 | `,
42 | [
43 | '--ontology',
44 | `https://fake.com/${basename(import.meta.url)}.nt`,
45 | `--verbose`,
46 | ],
47 | );
48 |
49 | expect(actual).toMatchInlineSnapshot(`
50 | "/** Used at the top-level node to indicate the context for the JSON-LD objects used. The context provided in this type is compatible with the keys and URLs in the rest of this generated file. */
51 | export type WithContext = T & {
52 | "@context": "https://schema.org";
53 | };
54 | export interface Graph {
55 | "@context": "https://schema.org";
56 | "@graph": readonly Thing[];
57 | }
58 | type SchemaValue = T | readonly T[];
59 | type IdReference = {
60 | /** IRI identifying the canonical address of this object. */
61 | "@id": string;
62 | };
63 | type InputActionConstraints = Partial<{
64 | [K in Exclude as \`\${string & K}-input\`]: PropertyValueSpecification | string;
65 | }>;
66 | type OutputActionConstraints = Partial<{
67 | [K in Exclude as \`\${string & K}-output\`]: PropertyValueSpecification | string;
68 | }>;
69 | /** Provides input and output action constraints for an action. */
70 | export type WithActionConstraints = T & InputActionConstraints & OutputActionConstraints;
71 |
72 | export type Text = string;
73 |
74 | interface ThingBase extends Partial {
75 | /**
76 | * Names are great!
77 | * {@link X Y}
78 | */
79 | "name"?: SchemaValue;
80 | }
81 | interface ThingLeaf extends ThingBase {
82 | "@type": "Thing";
83 | }
84 | /**
85 | * Things are amazing!
86 | *
87 | * - Foo
88 | * - Bar
89 | * - _Baz_, and __Bat__
90 | */
91 | export type Thing = "http://schema.org/Gadget" | "https://schema.org/Gadget" | "Gadget" | "http://schema.org/Widget" | "https://schema.org/Widget" | "Widget" | ThingLeaf;
92 |
93 | "
94 | `);
95 | expect(actualLogs).toMatchInlineSnapshot(`
96 | "Loading Ontology from URL: https://fake.com/duplicate_comments_test.ts.nt
97 | Got Response 200: Ok.
98 | Duplicate comments provided on class http://schema.org/Thing. It will be overwritten.
99 | Duplicate comments provided on property http://schema.org/name. It will be overwritten.
100 | Duplicate comments provided on http://schema.org/Gadget enum but one already exists. It will be overwritten.
101 | "
102 | `);
103 | });
104 |
--------------------------------------------------------------------------------
/packages/schema-dts-gen/test/baselines/enum_skipped_test.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2023 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * @fileoverview Baseline tests are a set of tests (in tests/baseline/) that
17 | * correspond to full comparisons of a generate .ts output based on a set of
18 | * Triples representing an entire ontology.
19 | */
20 | import {basename} from 'path';
21 |
22 | import {inlineCli} from '../helpers/main_driver.js';
23 |
24 | test(`baseline_${basename(import.meta.url)}`, async () => {
25 | const {actual, actualLogs} = await inlineCli(
26 | `
27 | .
28 | .
29 | .
30 | .
31 | .
32 | .
33 | "A letter!" .
34 | "issue-1156" .
35 | .
36 | "A Thing!" .
37 | `,
38 | [
39 | '--ontology',
40 | `https://fake.com/${basename(import.meta.url)}.nt`,
41 | `--verbose`,
42 | ],
43 | );
44 |
45 | expect(actual).toMatchInlineSnapshot(`
46 | "/** Used at the top-level node to indicate the context for the JSON-LD objects used. The context provided in this type is compatible with the keys and URLs in the rest of this generated file. */
47 | export type WithContext = T & {
48 | "@context": "https://schema.org";
49 | };
50 | export interface Graph {
51 | "@context": "https://schema.org";
52 | "@graph": readonly Thing[];
53 | }
54 | type SchemaValue = T | readonly T[];
55 | type IdReference = {
56 | /** IRI identifying the canonical address of this object. */
57 | "@id": string;
58 | };
59 | type InputActionConstraints = Partial<{
60 | [K in Exclude as \`\${string & K}-input\`]: PropertyValueSpecification | string;
61 | }>;
62 | type OutputActionConstraints = Partial<{
63 | [K in Exclude as \`\${string & K}-output\`]: PropertyValueSpecification | string;
64 | }>;
65 | /** Provides input and output action constraints for an action. */
66 | export type WithActionConstraints = T & InputActionConstraints & OutputActionConstraints;
67 |
68 | interface ThingBase extends Partial {
69 | }
70 | interface ThingLeaf extends ThingBase {
71 | "@type": "Thing";
72 | }
73 | /** A Thing! */
74 | export type Thing = "http://schema.org/a" | "https://schema.org/a" | "a" | "http://schema.org/b" | "https://schema.org/b" | "b" | "http://schema.org/c" | "https://schema.org/c" | "c" | "http://schema.org/d" | "https://schema.org/d" | "d" | "https://schema.org/e" | "e" | "http://google.com/f" | "https://google.com/f" | ThingLeaf;
75 |
76 | "
77 | `);
78 | expect(actualLogs).toMatchInlineSnapshot(`
79 | "Loading Ontology from URL: https://fake.com/enum_skipped_test.ts.nt
80 | Got Response 200: Ok.
81 | For Enum Item c, did not process:
82 | (category, issue-1156)
83 | "
84 | `);
85 | });
86 |
--------------------------------------------------------------------------------
/packages/schema-dts-gen/test/baselines/inheritance_multiple_test.ts:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright 2023 Google LLC
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * https://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | *
16 | * @fileoverview Baseline tests are a set of tests (in tests/baseline/) that
17 | * correspond to full comparisons of a generate .ts output based on a set of
18 | * Triples representing an entire ontology.
19 | */
20 | import {basename} from 'path';
21 |
22 | import {inlineCli} from '../helpers/main_driver.js';
23 |
24 | test(`baseline_${basename(import.meta.url)}`, async () => {
25 | const {actual} = await inlineCli(
26 | `
27 | .
28 | .
29 | .
30 | .
31 | .
32 | .
33 | .
34 | .
35 | .
36 | .
37 | .
38 |