create-backbone-template 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (182) hide show
  1. package/README.md +33 -0
  2. package/bin/create-backbone-template.js +5 -0
  3. package/package.json +30 -0
  4. package/src/create-backbone-template.js +204 -0
  5. package/template/.agents/skills/agent-browser/SKILL.md +55 -0
  6. package/template/.agents/skills/create-plan/SKILL.md +52 -0
  7. package/template/.agents/skills/create-plan/agents/openai.yaml +4 -0
  8. package/template/.agents/skills/create-pr-presentation/SKILL.md +86 -0
  9. package/template/.agents/skills/create-pr-presentation/agents/openai.yaml +4 -0
  10. package/template/.agents/skills/implement-plan/SKILL.md +26 -0
  11. package/template/.agents/skills/implement-plan/agents/openai.yaml +4 -0
  12. package/template/.agents/skills/review-plan/SKILL.md +38 -0
  13. package/template/.agents/skills/review-plan/agents/openai.yaml +4 -0
  14. package/template/.env.schema +30 -0
  15. package/template/.env.test +6 -0
  16. package/template/.oxlintrc.json +67 -0
  17. package/template/.vscode/extensions.json +3 -0
  18. package/template/.vscode/settings.json +23 -0
  19. package/template/AGENTS.md +55 -0
  20. package/template/Cargo.lock +2648 -0
  21. package/template/Cargo.toml +29 -0
  22. package/template/Justfile +140 -0
  23. package/template/README.md +72 -0
  24. package/template/TODO.md +1 -0
  25. package/template/_gitignore +12 -0
  26. package/template/buf.gen.yaml +7 -0
  27. package/template/buf.yaml +10 -0
  28. package/template/client/.oxfmtrc.json +8 -0
  29. package/template/client/.oxlintrc.json +57 -0
  30. package/template/client/README.md +19 -0
  31. package/template/client/_gitignore +5 -0
  32. package/template/client/index.html +12 -0
  33. package/template/client/package.json +47 -0
  34. package/template/client/packages/design-system/package.json +19 -0
  35. package/template/client/packages/design-system/src/index.ts +2 -0
  36. package/template/client/packages/design-system-basic/package.json +18 -0
  37. package/template/client/packages/design-system-basic/src/button.stories.tsx +50 -0
  38. package/template/client/packages/design-system-basic/src/button.tsx +26 -0
  39. package/template/client/packages/design-system-basic/src/empty-state.stories.tsx +18 -0
  40. package/template/client/packages/design-system-basic/src/empty-state.tsx +17 -0
  41. package/template/client/packages/design-system-basic/src/form-field.stories.tsx +15 -0
  42. package/template/client/packages/design-system-basic/src/form-field.tsx +10 -0
  43. package/template/client/packages/design-system-basic/src/form.stories.tsx +27 -0
  44. package/template/client/packages/design-system-basic/src/form.tsx +9 -0
  45. package/template/client/packages/design-system-basic/src/heading.stories.tsx +14 -0
  46. package/template/client/packages/design-system-basic/src/heading.tsx +25 -0
  47. package/template/client/packages/design-system-basic/src/index.tsx +15 -0
  48. package/template/client/packages/design-system-basic/src/inline.stories.tsx +13 -0
  49. package/template/client/packages/design-system-basic/src/inline.tsx +5 -0
  50. package/template/client/packages/design-system-basic/src/layout.stories.tsx +24 -0
  51. package/template/client/packages/design-system-basic/src/layout.tsx +14 -0
  52. package/template/client/packages/design-system-basic/src/loader.stories.tsx +8 -0
  53. package/template/client/packages/design-system-basic/src/loader.tsx +11 -0
  54. package/template/client/packages/design-system-basic/src/navigation.stories.tsx +16 -0
  55. package/template/client/packages/design-system-basic/src/navigation.tsx +18 -0
  56. package/template/client/packages/design-system-basic/src/notice.stories.tsx +13 -0
  57. package/template/client/packages/design-system-basic/src/notice.tsx +5 -0
  58. package/template/client/packages/design-system-basic/src/stack.stories.tsx +17 -0
  59. package/template/client/packages/design-system-basic/src/stack.tsx +5 -0
  60. package/template/client/packages/design-system-basic/src/styles.css +254 -0
  61. package/template/client/packages/design-system-basic/src/text-input.stories.tsx +13 -0
  62. package/template/client/packages/design-system-basic/src/text-input.tsx +5 -0
  63. package/template/client/packages/design-system-basic/src/text.stories.tsx +21 -0
  64. package/template/client/packages/design-system-basic/src/text.tsx +5 -0
  65. package/template/client/packages/design-system-contract/package.json +15 -0
  66. package/template/client/packages/design-system-contract/src/button.ts +10 -0
  67. package/template/client/packages/design-system-contract/src/empty-state.ts +9 -0
  68. package/template/client/packages/design-system-contract/src/form-field.ts +9 -0
  69. package/template/client/packages/design-system-contract/src/form.ts +9 -0
  70. package/template/client/packages/design-system-contract/src/heading.ts +9 -0
  71. package/template/client/packages/design-system-contract/src/index.ts +13 -0
  72. package/template/client/packages/design-system-contract/src/inline.ts +7 -0
  73. package/template/client/packages/design-system-contract/src/layout.ts +8 -0
  74. package/template/client/packages/design-system-contract/src/loader.ts +7 -0
  75. package/template/client/packages/design-system-contract/src/navigation.ts +13 -0
  76. package/template/client/packages/design-system-contract/src/notice.ts +8 -0
  77. package/template/client/packages/design-system-contract/src/stack.ts +8 -0
  78. package/template/client/packages/design-system-contract/src/text-input.ts +5 -0
  79. package/template/client/packages/design-system-contract/src/text.ts +9 -0
  80. package/template/client/packages/design-system-lint/fixtures/invalid/external-ui-import.tsx +5 -0
  81. package/template/client/packages/design-system-lint/fixtures/invalid/raw-dom-jsx.tsx +3 -0
  82. package/template/client/packages/design-system-lint/fixtures/invalid/two-violations.tsx +7 -0
  83. package/template/client/packages/design-system-lint/fixtures/valid/design-system-only.tsx +13 -0
  84. package/template/client/packages/design-system-lint/package.json +23 -0
  85. package/template/client/packages/design-system-lint/src/check-design-system-architecture.ts +22 -0
  86. package/template/client/packages/design-system-lint/src/design-system-architecture.ts +286 -0
  87. package/template/client/packages/design-system-lint/src/oxlint-plugin.ts +11 -0
  88. package/template/client/packages/design-system-lint/src/page-architecture.ts +382 -0
  89. package/template/client/packages/design-system-lint/src/rules.ts +111 -0
  90. package/template/client/packages/design-system-lint/test/design-system-architecture.test.ts +243 -0
  91. package/template/client/packages/design-system-lint/test/oxlint-fixtures.test.ts +159 -0
  92. package/template/client/packages/design-system-lint/test/page-architecture.test.ts +175 -0
  93. package/template/client/packages/design-system-lint/test/rules.test.ts +65 -0
  94. package/template/client/packages/design-system-lint/tsconfig.json +29 -0
  95. package/template/client/src/App.tsx +77 -0
  96. package/template/client/src/design-system-components.test.tsx +75 -0
  97. package/template/client/src/gen/helloworld/v1/helloworld_pb.ts +63 -0
  98. package/template/client/src/main.tsx +18 -0
  99. package/template/client/src/pages/hello/hello-page.stories.tsx +20 -0
  100. package/template/client/src/pages/hello/hello-page.test.tsx +90 -0
  101. package/template/client/src/pages/hello/hello-page.tsx +126 -0
  102. package/template/client/src/pages/page.ts +20 -0
  103. package/template/client/src/testing/create-preview-events.test.ts +36 -0
  104. package/template/client/src/testing/create-preview-events.ts +30 -0
  105. package/template/client/src/vite-env.d.ts +1 -0
  106. package/template/client/tsconfig.json +32 -0
  107. package/template/client/vite.config.ts +21 -0
  108. package/template/client/vite.ladle.config.ts +5 -0
  109. package/template/e2e/.gherkin-lintrc +20 -0
  110. package/template/e2e/.oxfmtrc.json +15 -0
  111. package/template/e2e/.oxlintrc.json +37 -0
  112. package/template/e2e/_gitignore +4 -0
  113. package/template/e2e/features/helloworld.feature +10 -0
  114. package/template/e2e/package.json +42 -0
  115. package/template/e2e/playwright.config.ts +16 -0
  116. package/template/e2e/support/app-gherkin.ts +4 -0
  117. package/template/e2e/support/fixtures.ts +236 -0
  118. package/template/e2e/support/gherkin-fixtures/duplicate-id.feature +9 -0
  119. package/template/e2e/support/gherkin-fixtures/duplicate-id.spec.ts +7 -0
  120. package/template/e2e/support/gherkin-fixtures/extra-implementation.spec.ts +7 -0
  121. package/template/e2e/support/gherkin-fixtures/extra-step.spec.ts +10 -0
  122. package/template/e2e/support/gherkin-fixtures/happy-path.spec.ts +4 -0
  123. package/template/e2e/support/gherkin-fixtures/missing-id.feature +4 -0
  124. package/template/e2e/support/gherkin-fixtures/missing-id.spec.ts +7 -0
  125. package/template/e2e/support/gherkin-fixtures/missing-implementation.spec.ts +7 -0
  126. package/template/e2e/support/gherkin-fixtures/missing-step.spec.ts +7 -0
  127. package/template/e2e/support/gherkin-fixtures/playwright.config.ts +7 -0
  128. package/template/e2e/support/gherkin-fixtures/scenario-outline.feature +9 -0
  129. package/template/e2e/support/gherkin-fixtures/scenario-outline.spec.ts +7 -0
  130. package/template/e2e/support/gherkin-fixtures/step-mismatch.spec.ts +9 -0
  131. package/template/e2e/support/gherkin-fixtures/valid-implementations.ts +23 -0
  132. package/template/e2e/support/gherkin-fixtures/valid-scenarios.feature +26 -0
  133. package/template/e2e/support/gherkin.test.ts +184 -0
  134. package/template/e2e/support/gherkin.ts +321 -0
  135. package/template/e2e/support/oxlint-plugin.test.ts +328 -0
  136. package/template/e2e/support/oxlint-plugin.ts +485 -0
  137. package/template/e2e/tests/helloworld.spec.ts +39 -0
  138. package/template/e2e/tsconfig.json +26 -0
  139. package/template/e2e/tsconfig.oxlint-plugin.json +12 -0
  140. package/template/package.json +9 -0
  141. package/template/pnpm-lock.yaml +10723 -0
  142. package/template/pnpm-workspace.yaml +8 -0
  143. package/template/pr-slide/README.md +95 -0
  144. package/template/pr-slide/package.json +23 -0
  145. package/template/pr-slide/src/cli.js +262 -0
  146. package/template/pr-slide/src/generate-pr-deck.js +833 -0
  147. package/template/pr-slide/src/git-context.js +91 -0
  148. package/template/pr-slide/src/presentation-paths.js +9 -0
  149. package/template/pr-slide/src/presentations.js +53 -0
  150. package/template/pr-slide/test/generate-pr-deck.test.js +118 -0
  151. package/template/pr-slide/test/presentation-paths.test.js +14 -0
  152. package/template/pr-slide/test/presentations.test.js +50 -0
  153. package/template/proto/helloworld/v1/helloworld.proto +15 -0
  154. package/template/scripts/run-e2e.sh +10 -0
  155. package/template/server/Cargo.toml +26 -0
  156. package/template/server/build.rs +9 -0
  157. package/template/server/dylint/backbone_server_lints/.cargo/config.toml +6 -0
  158. package/template/server/dylint/backbone_server_lints/Cargo.lock +1581 -0
  159. package/template/server/dylint/backbone_server_lints/Cargo.toml +21 -0
  160. package/template/server/dylint/backbone_server_lints/README.md +5 -0
  161. package/template/server/dylint/backbone_server_lints/_gitignore +1 -0
  162. package/template/server/dylint/backbone_server_lints/rust-toolchain +3 -0
  163. package/template/server/dylint/backbone_server_lints/src/lib.rs +612 -0
  164. package/template/server/dylint/backbone_server_lints/ui/lib.rs +4 -0
  165. package/template/server/dylint/backbone_server_lints/ui/lib.stderr +10 -0
  166. package/template/server/dylint/backbone_server_lints/ui/long_file.rs +303 -0
  167. package/template/server/dylint/backbone_server_lints/ui/long_file.stderr +6 -0
  168. package/template/server/dylint/backbone_server_lints/ui/main.rs +59 -0
  169. package/template/server/dylint/backbone_server_lints/ui/main.stderr +85 -0
  170. package/template/server/migrations/20260520120000_create_projects.sql +12 -0
  171. package/template/server/migrations/20260524160000_create_hello_world_inputs.sql +12 -0
  172. package/template/server/src/config.rs +27 -0
  173. package/template/server/src/db/hello_world.rs +34 -0
  174. package/template/server/src/db/hello_world_tests.rs +11 -0
  175. package/template/server/src/db/mod.rs +39 -0
  176. package/template/server/src/lib.rs +10 -0
  177. package/template/server/src/main.rs +43 -0
  178. package/template/server/src/rpc/greeter/mod.rs +31 -0
  179. package/template/server/src/rpc/greeter/say_hello.rs +27 -0
  180. package/template/server/src/rpc/mod.rs +8 -0
  181. package/template/server/src/state.rs +13 -0
  182. package/template/skills-lock.json +11 -0
@@ -0,0 +1,5 @@
1
+ import type { ComponentType, InputHTMLAttributes } from "react"
2
+
3
+ export type TextInputProps = InputHTMLAttributes<HTMLInputElement>
4
+
5
+ export type TextInputComponent = ComponentType<TextInputProps>
@@ -0,0 +1,9 @@
1
+ import type { ComponentType, ReactNode } from "react"
2
+
3
+ export type TextProps = {
4
+ children: ReactNode
5
+ tone?: "default" | "muted" | "accent" | "danger"
6
+ variant?: "body" | "lede" | "eyebrow"
7
+ }
8
+
9
+ export type TextComponent = ComponentType<TextProps>
@@ -0,0 +1,5 @@
1
+ import { Button } from "@mui/material"
2
+
3
+ export function ExternalUiImport() {
4
+ return <Button type="button">Save</Button>
5
+ }
@@ -0,0 +1,3 @@
1
+ export function RawDomJsx() {
2
+ return <button type="button">Save</button>
3
+ }
@@ -0,0 +1,7 @@
1
+ import styled from "styled-components"
2
+
3
+ const Panel = styled.div``
4
+
5
+ export function TwoViolations() {
6
+ return <main>{Panel.toString()}</main>
7
+ }
@@ -0,0 +1,13 @@
1
+ import { Button, Layout, Text } from "@backbone/design-system"
2
+
3
+ export function ValidApp() {
4
+ return (
5
+ <Layout
6
+ middle={
7
+ <Button type="button">
8
+ <Text>Save</Text>
9
+ </Button>
10
+ }
11
+ />
12
+ )
13
+ }
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "@backbone/design-system-lint",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "exports": {
7
+ ".": {
8
+ "types": "./dist/src/oxlint-plugin.d.ts",
9
+ "default": "./dist/src/oxlint-plugin.js"
10
+ }
11
+ },
12
+ "scripts": {
13
+ "build": "tsc --project tsconfig.json",
14
+ "check:architecture": "node dist/src/check-design-system-architecture.js",
15
+ "test": "pnpm run build && node --test \"dist/test/**/*.test.js\"",
16
+ "test:prepared": "node --test \"dist/test/**/*.test.js\""
17
+ },
18
+ "devDependencies": {
19
+ "@types/node": "^25.9.1",
20
+ "oxlint": "^1.66.0",
21
+ "typescript": "^6.0.3"
22
+ }
23
+ }
@@ -0,0 +1,22 @@
1
+ import path from "node:path"
2
+ import { checkDesignSystemArchitecture } from "./design-system-architecture.js"
3
+ import { checkPageArchitecture } from "./page-architecture.js"
4
+
5
+ const designSystemResult = checkDesignSystemArchitecture({
6
+ contractSrcDir: path.resolve("../design-system-contract/src"),
7
+ implementationSrcDir: path.resolve("../design-system-basic/src"),
8
+ })
9
+
10
+ const pageResult = checkPageArchitecture({
11
+ pagesSrcDir: path.resolve("../../src/pages"),
12
+ })
13
+
14
+ const errors = [...designSystemResult.errors, ...pageResult.errors]
15
+
16
+ if (errors.length > 0) {
17
+ for (const error of errors) {
18
+ console.error(error)
19
+ }
20
+
21
+ process.exitCode = 1
22
+ }
@@ -0,0 +1,286 @@
1
+ import fs from "node:fs"
2
+ import path from "node:path"
3
+ import ts from "typescript"
4
+
5
+ export type DesignSystemArchitecturePaths = {
6
+ contractSrcDir: string
7
+ implementationSrcDir: string
8
+ }
9
+
10
+ export type DesignSystemArchitectureResult = {
11
+ errors: string[]
12
+ }
13
+
14
+ type ComponentFile = {
15
+ absolutePath: string
16
+ componentPath: string
17
+ relativePath: string
18
+ }
19
+
20
+ export function checkDesignSystemArchitecture(
21
+ paths: DesignSystemArchitecturePaths,
22
+ ): DesignSystemArchitectureResult {
23
+ const contractFiles = findComponentFiles(paths.contractSrcDir, ".ts")
24
+ const implementationFiles = findComponentFiles(paths.implementationSrcDir, ".tsx")
25
+ const errors = [
26
+ ...checkMatchingComponentFiles(contractFiles, implementationFiles),
27
+ ...checkBarrelExports(paths.contractSrcDir, "index.ts", contractFiles, "Contract"),
28
+ ...checkBarrelExports(
29
+ paths.implementationSrcDir,
30
+ "index.tsx",
31
+ implementationFiles,
32
+ "Implementation",
33
+ ),
34
+ ...checkContractFiles(contractFiles),
35
+ ...checkImplementationFiles(implementationFiles, paths.implementationSrcDir),
36
+ ]
37
+
38
+ return { errors }
39
+ }
40
+
41
+ function findComponentFiles(srcDir: string, extension: ".ts" | ".tsx") {
42
+ if (!fs.existsSync(srcDir)) {
43
+ return []
44
+ }
45
+
46
+ return findFiles(srcDir)
47
+ .filter((filePath) => isComponentFile(filePath, extension))
48
+ .map((absolutePath) => {
49
+ const relativePath = normalizePath(path.relative(srcDir, absolutePath))
50
+ const componentPath = relativePath.slice(0, -extension.length)
51
+
52
+ return { absolutePath, componentPath, relativePath }
53
+ })
54
+ .sort((left, right) => left.componentPath.localeCompare(right.componentPath))
55
+ }
56
+
57
+ function findFiles(rootDir: string): string[] {
58
+ const entries = fs.readdirSync(rootDir, { withFileTypes: true })
59
+ const files: string[] = []
60
+
61
+ for (const entry of entries) {
62
+ const absolutePath = path.join(rootDir, entry.name)
63
+
64
+ if (entry.isDirectory()) {
65
+ files.push(...findFiles(absolutePath))
66
+ } else if (entry.isFile()) {
67
+ files.push(absolutePath)
68
+ }
69
+ }
70
+
71
+ return files
72
+ }
73
+
74
+ function isComponentFile(filePath: string, extension: ".ts" | ".tsx") {
75
+ const fileName = path.basename(filePath)
76
+
77
+ return (
78
+ fileName !== `index${extension}` &&
79
+ !fileName.endsWith(".stories.tsx") &&
80
+ fileName.endsWith(extension)
81
+ )
82
+ }
83
+
84
+ function checkMatchingComponentFiles(
85
+ contractFiles: ComponentFile[],
86
+ implementationFiles: ComponentFile[],
87
+ ) {
88
+ const errors: string[] = []
89
+ const contractComponentPaths = new Set(contractFiles.map((file) => file.componentPath))
90
+ const implementationComponentPaths = new Set(
91
+ implementationFiles.map((file) => file.componentPath),
92
+ )
93
+
94
+ for (const componentPath of implementationComponentPaths) {
95
+ if (!contractComponentPaths.has(componentPath)) {
96
+ errors.push(
97
+ `Component "${componentPath}" exists in implementation but is missing from contract.`,
98
+ )
99
+ }
100
+ }
101
+
102
+ for (const componentPath of contractComponentPaths) {
103
+ if (!implementationComponentPaths.has(componentPath)) {
104
+ errors.push(
105
+ `Component "${componentPath}" exists in contract but is missing from implementation.`,
106
+ )
107
+ }
108
+ }
109
+
110
+ return errors
111
+ }
112
+
113
+ function checkContractFiles(files: ComponentFile[]) {
114
+ return files.flatMap(checkContractExports)
115
+ }
116
+
117
+ function checkImplementationFiles(files: ComponentFile[], implementationSrcDir: string) {
118
+ return files.flatMap((file) => [
119
+ ...checkImplementationExport(file),
120
+ ...checkColocatedStory(file, implementationSrcDir),
121
+ ])
122
+ }
123
+
124
+ function checkBarrelExports(
125
+ srcDir: string,
126
+ barrelFileName: "index.ts" | "index.tsx",
127
+ componentFiles: ComponentFile[],
128
+ packageName: "Contract" | "Implementation",
129
+ ) {
130
+ const barrelRelativePath = barrelFileName
131
+ const barrelAbsolutePath = path.join(srcDir, barrelFileName)
132
+ const expectedExports = componentFiles.map((file) => `./${file.componentPath}`).sort()
133
+ const actualExports = getBarrelExports(barrelAbsolutePath)
134
+ const errors: string[] = []
135
+
136
+ for (const exportPath of expectedExports) {
137
+ if (!actualExports.has(exportPath)) {
138
+ errors.push(`${packageName} barrel "${barrelRelativePath}" must export "${exportPath}".`)
139
+ }
140
+ }
141
+
142
+ for (const exportPath of actualExports) {
143
+ if (!expectedExports.includes(exportPath)) {
144
+ errors.push(`${packageName} barrel "${barrelRelativePath}" must not export "${exportPath}".`)
145
+ }
146
+ }
147
+
148
+ return errors
149
+ }
150
+
151
+ function checkContractExports(file: ComponentFile) {
152
+ const actualExports = getNamedExports(file.absolutePath)
153
+ const componentName = toPascalCase(path.basename(file.componentPath))
154
+ const expectedExports = [`${componentName}Component`, `${componentName}Props`].sort()
155
+
156
+ if (
157
+ actualExports.length === 2 &&
158
+ [...actualExports].sort().join("\n") === expectedExports.join("\n")
159
+ ) {
160
+ return []
161
+ }
162
+
163
+ return [
164
+ `Contract component file "${file.relativePath}" must export exactly "${expectedExports[0]}" and "${expectedExports[1]}".`,
165
+ ]
166
+ }
167
+
168
+ function checkImplementationExport(file: ComponentFile) {
169
+ const actualExports = getNamedExports(file.absolutePath)
170
+ const expectedExport = toPascalCase(path.basename(file.componentPath))
171
+
172
+ if (actualExports.length === 1 && actualExports[0] === expectedExport) {
173
+ return []
174
+ }
175
+
176
+ return [
177
+ `Implementation component file "${file.relativePath}" must export exactly "${expectedExport}".`,
178
+ ]
179
+ }
180
+
181
+ function checkColocatedStory(file: ComponentFile, implementationSrcDir: string) {
182
+ const expectedStoryPath = `${file.componentPath}.stories.tsx`
183
+ const absoluteStoryPath = path.join(implementationSrcDir, expectedStoryPath)
184
+
185
+ if (fs.existsSync(absoluteStoryPath)) {
186
+ return []
187
+ }
188
+
189
+ return [
190
+ `Implementation component "${file.componentPath}" must have a colocated story file at "${expectedStoryPath}".`,
191
+ ]
192
+ }
193
+
194
+ function getBarrelExports(filePath: string) {
195
+ if (!fs.existsSync(filePath)) {
196
+ return new Set<string>()
197
+ }
198
+
199
+ const sourceText = fs.readFileSync(filePath, "utf8")
200
+ const sourceFile = ts.createSourceFile(filePath, sourceText, ts.ScriptTarget.Latest, true)
201
+ const exports = new Set<string>()
202
+
203
+ for (const statement of sourceFile.statements) {
204
+ if (!ts.isExportDeclaration(statement) || statement.moduleSpecifier === undefined) {
205
+ continue
206
+ }
207
+
208
+ if (ts.isStringLiteral(statement.moduleSpecifier)) {
209
+ exports.add(statement.moduleSpecifier.text)
210
+ }
211
+ }
212
+
213
+ return exports
214
+ }
215
+
216
+ function getNamedExports(filePath: string) {
217
+ const sourceText = fs.readFileSync(filePath, "utf8")
218
+ const sourceFile = ts.createSourceFile(filePath, sourceText, ts.ScriptTarget.Latest, true)
219
+ const exports: string[] = []
220
+
221
+ for (const statement of sourceFile.statements) {
222
+ const exportedDeclarationName = getExportedDeclarationName(statement)
223
+
224
+ if (exportedDeclarationName !== undefined) {
225
+ exports.push(exportedDeclarationName)
226
+ } else if (ts.isExportDeclaration(statement) && statement.exportClause !== undefined) {
227
+ if (ts.isNamedExports(statement.exportClause)) {
228
+ exports.push(...statement.exportClause.elements.map((element) => element.name.text))
229
+ } else {
230
+ exports.push("*")
231
+ }
232
+ }
233
+ }
234
+
235
+ return exports.sort()
236
+ }
237
+
238
+ function getExportedDeclarationName(statement: ts.Statement) {
239
+ if (!hasExportModifier(statement)) {
240
+ return undefined
241
+ }
242
+
243
+ if (ts.isVariableStatement(statement)) {
244
+ const firstDeclaration = statement.declarationList.declarations[0]
245
+
246
+ if (
247
+ firstDeclaration !== undefined &&
248
+ statement.declarationList.declarations.length === 1 &&
249
+ ts.isIdentifier(firstDeclaration.name)
250
+ ) {
251
+ return firstDeclaration.name.text
252
+ }
253
+ }
254
+
255
+ if (
256
+ (ts.isFunctionDeclaration(statement) ||
257
+ ts.isClassDeclaration(statement) ||
258
+ ts.isInterfaceDeclaration(statement) ||
259
+ ts.isTypeAliasDeclaration(statement)) &&
260
+ statement.name !== undefined
261
+ ) {
262
+ return statement.name.text
263
+ }
264
+
265
+ return "*"
266
+ }
267
+
268
+ function hasExportModifier(statement: ts.Statement) {
269
+ return (
270
+ ts.canHaveModifiers(statement) &&
271
+ ts
272
+ .getModifiers(statement)
273
+ ?.some((modifier) => modifier.kind === ts.SyntaxKind.ExportKeyword) === true
274
+ )
275
+ }
276
+
277
+ function toPascalCase(fileName: string) {
278
+ return fileName
279
+ .split("-")
280
+ .map((part) => part.charAt(0).toUpperCase() + part.slice(1))
281
+ .join("")
282
+ }
283
+
284
+ function normalizePath(filePath: string) {
285
+ return filePath.split(path.sep).join("/")
286
+ }
@@ -0,0 +1,11 @@
1
+ import { noExternalUiImports, noRawDomJsx } from "./rules.js"
2
+
3
+ export default {
4
+ meta: {
5
+ name: "backbone-design-system",
6
+ },
7
+ rules: {
8
+ "no-external-ui-imports": noExternalUiImports,
9
+ "no-raw-dom-jsx": noRawDomJsx,
10
+ },
11
+ }