├── .gitignore ├── README.md ├── examples └── solidjs-with-layout │ ├── .dependency-cruiser.js │ ├── .gitignore │ ├── README.md │ ├── dependency-graph-preview.svg │ ├── eslint.config.mjs │ ├── index.html │ ├── package.json │ ├── pnpm-lock.yaml │ ├── public │ └── favicon.png │ ├── src │ ├── app │ │ ├── App.tsx │ │ ├── entry.css │ │ ├── entry.tsx │ │ └── layouts │ │ │ ├── BaseLayout.tsx │ │ │ └── index.ts │ ├── pages │ │ ├── 404.tsx │ │ ├── about.tsx │ │ └── home.tsx │ ├── shared │ │ └── ui │ │ │ ├── Layout │ │ │ ├── Layout.module.css │ │ │ └── Layout.tsx │ │ │ └── index.ts │ └── widgets │ │ └── header │ │ ├── index.ts │ │ └── ui │ │ ├── Header.module.css │ │ └── Header.tsx │ ├── tsconfig.json │ └── vite.config.mts ├── package.json ├── pnpm-lock.yaml ├── pnpm-workspace.yaml └── todo-app ├── .env ├── .gitignore ├── README.md ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt ├── src ├── app │ ├── index.scss │ ├── index.tsx │ ├── providers │ │ ├── index.ts │ │ └── with-router.tsx │ └── styles │ │ ├── index.scss │ │ ├── normalize-antd.scss │ │ ├── normalize.scss │ │ └── vars.scss ├── entities │ └── task │ │ ├── index.ts │ │ ├── lib.ts │ │ ├── model │ │ ├── index.ts │ │ └── tasks.ts │ │ └── ui │ │ ├── index.ts │ │ ├── task-card │ │ ├── index.tsx │ │ └── styles.module.scss │ │ └── task-row │ │ ├── index.tsx │ │ └── styles.module.scss ├── features │ ├── tasks-filters │ │ ├── config.ts │ │ ├── index.ts │ │ └── ui.tsx │ └── toggle-task │ │ ├── index.ts │ │ ├── model │ │ ├── index.ts │ │ └── toggle-task.ts │ │ └── ui.tsx ├── index.tsx ├── pages │ ├── index.tsx │ ├── task-details │ │ ├── index.tsx │ │ └── styles.module.scss │ └── tasks-list │ │ ├── index.tsx │ │ └── styles.module.scss ├── react-app-env.d.ts ├── reportWebVitals.ts ├── setupTests.ts └── shared │ ├── api │ ├── index.ts │ ├── models.ts │ └── typicode │ │ ├── base.ts │ │ ├── index.ts │ │ ├── models.ts │ │ └── tasks.ts │ └── config │ └── index.ts ├── tsconfig.json └── yarn.lock /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | .DS_Store 3 | .vscode 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Feature-Sliced Design examples 2 | 3 | A collection of Feature-Sliced Examples that show how you can build applications with Feature-Sliced Design. 4 | 5 | ## List 6 | 7 | - **[examples/react-effector-todo-app](./todo-app)** [outdated] 8 | - **[examples/solidjs-with-layout](./examples/solidjs-with-layout)** - shows how to work with layouts 9 | 10 | ## Community examples — full projects 11 | 12 | You can see community contributed examples [in the showcase on the official site](https://feature-sliced.github.io/documentation/examples). 13 | -------------------------------------------------------------------------------- /examples/solidjs-with-layout/.dependency-cruiser.js: -------------------------------------------------------------------------------- 1 | /** @type {import('dependency-cruiser').IConfiguration} */ 2 | module.exports = { 3 | forbidden: [ 4 | { 5 | name: 'no-circular', 6 | severity: 'warn', 7 | comment: 8 | 'This dependency is part of a circular relationship. You might want to revise ' + 9 | 'your solution (i.e. use dependency inversion, make sure the modules have a single responsibility) ', 10 | from: {}, 11 | to: { 12 | circular: true 13 | } 14 | }, 15 | { 16 | name: 'no-orphans', 17 | comment: 18 | "This is an orphan module - it's likely not used (anymore?). Either use it or " + 19 | "remove it. If it's logical this module is an orphan (i.e. it's a config file), " + 20 | "add an exception for it in your dependency-cruiser configuration. By default " + 21 | "this rule does not scrutinize dot-files (e.g. .eslintrc.js), TypeScript declaration " + 22 | "files (.d.ts), tsconfig.json and some of the babel and webpack configs.", 23 | severity: 'warn', 24 | from: { 25 | orphan: true, 26 | pathNot: [ 27 | '(^|/)[.][^/]+[.](?:js|cjs|mjs|ts|cts|mts|json)$', // dot files 28 | '[.]d[.]ts$', // TypeScript declaration files 29 | '(^|/)tsconfig[.]json$', // TypeScript config 30 | '(^|/)(?:babel|webpack)[.]config[.](?:js|cjs|mjs|ts|cts|mts|json)$' // other configs 31 | ] 32 | }, 33 | to: {}, 34 | }, 35 | { 36 | name: 'no-deprecated-core', 37 | comment: 38 | 'A module depends on a node core module that has been deprecated. Find an alternative - these are ' + 39 | "bound to exist - node doesn't deprecate lightly.", 40 | severity: 'warn', 41 | from: {}, 42 | to: { 43 | dependencyTypes: [ 44 | 'core' 45 | ], 46 | path: [ 47 | '^v8/tools/codemap$', 48 | '^v8/tools/consarray$', 49 | '^v8/tools/csvparser$', 50 | '^v8/tools/logreader$', 51 | '^v8/tools/profile_view$', 52 | '^v8/tools/profile$', 53 | '^v8/tools/SourceMap$', 54 | '^v8/tools/splaytree$', 55 | '^v8/tools/tickprocessor-driver$', 56 | '^v8/tools/tickprocessor$', 57 | '^node-inspect/lib/_inspect$', 58 | '^node-inspect/lib/internal/inspect_client$', 59 | '^node-inspect/lib/internal/inspect_repl$', 60 | '^async_hooks$', 61 | '^punycode$', 62 | '^domain$', 63 | '^constants$', 64 | '^sys$', 65 | '^_linklist$', 66 | '^_stream_wrap$' 67 | ], 68 | } 69 | }, 70 | { 71 | name: 'not-to-deprecated', 72 | comment: 73 | 'This module uses a (version of an) npm module that has been deprecated. Either upgrade to a later ' + 74 | 'version of that module, or find an alternative. Deprecated modules are a security risk.', 75 | severity: 'warn', 76 | from: {}, 77 | to: { 78 | dependencyTypes: [ 79 | 'deprecated' 80 | ] 81 | } 82 | }, 83 | { 84 | name: 'no-non-package-json', 85 | severity: 'error', 86 | comment: 87 | "This module depends on an npm package that isn't in the 'dependencies' section of your package.json. " + 88 | "That's problematic as the package either (1) won't be available on live (2 - worse) will be " + 89 | "available on live with an non-guaranteed version. Fix it by adding the package to the dependencies " + 90 | "in your package.json.", 91 | from: {}, 92 | to: { 93 | dependencyTypes: [ 94 | 'npm-no-pkg', 95 | 'npm-unknown' 96 | ] 97 | } 98 | }, 99 | { 100 | name: 'not-to-unresolvable', 101 | comment: 102 | "This module depends on a module that cannot be found ('resolved to disk'). If it's an npm " + 103 | 'module: add it to your package.json. In all other cases you likely already know what to do.', 104 | severity: 'error', 105 | from: {}, 106 | to: { 107 | couldNotResolve: true 108 | } 109 | }, 110 | { 111 | name: 'no-duplicate-dep-types', 112 | comment: 113 | "Likely this module depends on an external ('npm') package that occurs more than once " + 114 | "in your package.json i.e. bot as a devDependencies and in dependencies. This will cause " + 115 | "maintenance problems later on.", 116 | severity: 'warn', 117 | from: {}, 118 | to: { 119 | moreThanOneDependencyType: true, 120 | // as it's pretty common to have a type import be a type only import 121 | // _and_ (e.g.) a devDependency - don't consider type-only dependency 122 | // types for this rule 123 | dependencyTypesNot: ["type-only"] 124 | } 125 | }, 126 | 127 | /* rules you might want to tweak for your specific situation: */ 128 | 129 | { 130 | name: 'not-to-spec', 131 | comment: 132 | 'This module depends on a spec (test) file. The sole responsibility of a spec file is to test code. ' + 133 | "If there's something in a spec that's of use to other modules, it doesn't have that single " + 134 | 'responsibility anymore. Factor it out into (e.g.) a separate utility/ helper or a mock.', 135 | severity: 'error', 136 | from: {}, 137 | to: { 138 | path: '[.](?:spec|test)[.](?:js|mjs|cjs|jsx|ts|mts|cts|tsx)$' 139 | } 140 | }, 141 | { 142 | name: 'not-to-dev-dep', 143 | severity: 'error', 144 | comment: 145 | "This module depends on an npm package from the 'devDependencies' section of your " + 146 | 'package.json. It looks like something that ships to production, though. To prevent problems ' + 147 | "with npm packages that aren't there on production declare it (only!) in the 'dependencies'" + 148 | 'section of your package.json. If this module is development only - add it to the ' + 149 | 'from.pathNot re of the not-to-dev-dep rule in the dependency-cruiser configuration', 150 | from: { 151 | path: '^(src)', 152 | pathNot: '[.](?:spec|test)[.](?:js|mjs|cjs|jsx|ts|mts|cts|tsx)$' 153 | }, 154 | to: { 155 | dependencyTypes: [ 156 | 'npm-dev', 157 | ], 158 | // type only dependencies are not a problem as they don't end up in the 159 | // production code or are ignored by the runtime. 160 | dependencyTypesNot: [ 161 | 'type-only' 162 | ], 163 | pathNot: [ 164 | 'node_modules/@types/' 165 | ] 166 | } 167 | }, 168 | { 169 | name: 'optional-deps-used', 170 | severity: 'info', 171 | comment: 172 | "This module depends on an npm package that is declared as an optional dependency " + 173 | "in your package.json. As this makes sense in limited situations only, it's flagged here. " + 174 | "If you're using an optional dependency here by design - add an exception to your" + 175 | "dependency-cruiser configuration.", 176 | from: {}, 177 | to: { 178 | dependencyTypes: [ 179 | 'npm-optional' 180 | ] 181 | } 182 | }, 183 | { 184 | name: 'peer-deps-used', 185 | comment: 186 | "This module depends on an npm package that is declared as a peer dependency " + 187 | "in your package.json. This makes sense if your package is e.g. a plugin, but in " + 188 | "other cases - maybe not so much. If the use of a peer dependency is intentional " + 189 | "add an exception to your dependency-cruiser configuration.", 190 | severity: 'warn', 191 | from: {}, 192 | to: { 193 | dependencyTypes: [ 194 | 'npm-peer' 195 | ] 196 | } 197 | } 198 | ], 199 | options: { 200 | 201 | /* Which modules not to follow further when encountered */ 202 | doNotFollow: { 203 | /* path: an array of regular expressions in strings to match against */ 204 | path: ['node_modules'] 205 | }, 206 | 207 | /* Which modules to exclude */ 208 | // exclude : { 209 | // /* path: an array of regular expressions in strings to match against */ 210 | // path: '', 211 | // }, 212 | 213 | /* Which modules to exclusively include (array of regular expressions in strings) 214 | dependency-cruiser will skip everything not matching this pattern 215 | */ 216 | // includeOnly : [''], 217 | 218 | /* List of module systems to cruise. 219 | When left out dependency-cruiser will fall back to the list of _all_ 220 | module systems it knows of. It's the default because it's the safe option 221 | It might come at a performance penalty, though. 222 | moduleSystems: ['amd', 'cjs', 'es6', 'tsd'] 223 | 224 | As in practice only commonjs ('cjs') and ecmascript modules ('es6') 225 | are widely used, you can limit the moduleSystems to those. 226 | */ 227 | 228 | // moduleSystems: ['cjs', 'es6'], 229 | 230 | /* prefix for links in html and svg output (e.g. 'https://github.com/you/yourrepo/blob/main/' 231 | to open it on your online repo or `vscode://file/${process.cwd()}/` to 232 | open it in visual studio code), 233 | */ 234 | // prefix: `vscode://file/${process.cwd()}/`, 235 | 236 | /* false (the default): ignore dependencies that only exist before typescript-to-javascript compilation 237 | true: also detect dependencies that only exist before typescript-to-javascript compilation 238 | "specify": for each dependency identify whether it only exists before compilation or also after 239 | */ 240 | tsPreCompilationDeps: true, 241 | 242 | /* list of extensions to scan that aren't javascript or compile-to-javascript. 243 | Empty by default. Only put extensions in here that you want to take into 244 | account that are _not_ parsable. 245 | */ 246 | // extraExtensionsToScan: [".json", ".jpg", ".png", ".svg", ".webp"], 247 | 248 | /* if true combines the package.jsons found from the module up to the base 249 | folder the cruise is initiated from. Useful for how (some) mono-repos 250 | manage dependencies & dependency definitions. 251 | */ 252 | // combinedDependencies: false, 253 | 254 | /* if true leave symlinks untouched, otherwise use the realpath */ 255 | // preserveSymlinks: false, 256 | 257 | /* TypeScript project file ('tsconfig.json') to use for 258 | (1) compilation and 259 | (2) resolution (e.g. with the paths property) 260 | 261 | The (optional) fileName attribute specifies which file to take (relative to 262 | dependency-cruiser's current working directory). When not provided 263 | defaults to './tsconfig.json'. 264 | */ 265 | tsConfig: { 266 | fileName: 'tsconfig.json' 267 | }, 268 | 269 | /* Webpack configuration to use to get resolve options from. 270 | 271 | The (optional) fileName attribute specifies which file to take (relative 272 | to dependency-cruiser's current working directory. When not provided defaults 273 | to './webpack.conf.js'. 274 | 275 | The (optional) `env` and `arguments` attributes contain the parameters 276 | to be passed if your webpack config is a function and takes them (see 277 | webpack documentation for details) 278 | */ 279 | // webpackConfig: { 280 | // fileName: 'webpack.config.js', 281 | // env: {}, 282 | // arguments: {} 283 | // }, 284 | 285 | /* Babel config ('.babelrc', '.babelrc.json', '.babelrc.json5', ...) to use 286 | for compilation 287 | */ 288 | // babelConfig: { 289 | // fileName: '.babelrc', 290 | // }, 291 | 292 | /* List of strings you have in use in addition to cjs/ es6 requires 293 | & imports to declare module dependencies. Use this e.g. if you've 294 | re-declared require, use a require-wrapper or use window.require as 295 | a hack. 296 | */ 297 | // exoticRequireStrings: [], 298 | 299 | /* options to pass on to enhanced-resolve, the package dependency-cruiser 300 | uses to resolve module references to disk. The values below should be 301 | suitable for most situations 302 | 303 | If you use webpack: you can also set these in webpack.conf.js. The set 304 | there will override the ones specified here. 305 | */ 306 | enhancedResolveOptions: { 307 | /* What to consider as an 'exports' field in package.jsons */ 308 | exportsFields: ["exports"], 309 | /* List of conditions to check for in the exports field. 310 | Only works when the 'exportsFields' array is non-empty. 311 | */ 312 | conditionNames: ["import", "require", "node", "default", "types"], 313 | /* 314 | The extensions, by default are the same as the ones dependency-cruiser 315 | can access (run `npx depcruise --info` to see which ones that are in 316 | _your_ environment). If that list is larger than you need you can pass 317 | the extensions you actually use (e.g. [".js", ".jsx"]). This can speed 318 | up module resolution, which is the most expensive step. 319 | */ 320 | // extensions: [".js", ".jsx", ".ts", ".tsx", ".d.ts"], 321 | /* What to consider a 'main' field in package.json */ 322 | 323 | // if you migrate to ESM (or are in an ESM environment already) you will want to 324 | // have "module" in the list of mainFields, like so: 325 | // mainFields: ["module", "main", "types", "typings"], 326 | mainFields: ["main", "types", "typings"], 327 | /* 328 | A list of alias fields in package.jsons 329 | See [this specification](https://github.com/defunctzombie/package-browser-field-spec) and 330 | the webpack [resolve.alias](https://webpack.js.org/configuration/resolve/#resolvealiasfields) 331 | documentation 332 | 333 | Defaults to an empty array (= don't use alias fields). 334 | */ 335 | // aliasFields: ["browser"], 336 | }, 337 | reporterOptions: { 338 | dot: { 339 | /* pattern of modules that can be consolidated in the detailed 340 | graphical dependency graph. The default pattern in this configuration 341 | collapses everything in node_modules to one folder deep so you see 342 | the external modules, but their innards. 343 | */ 344 | collapsePattern: 'node_modules/(?:@[^/]+/[^/]+|[^/]+)', 345 | 346 | /* Options to tweak the appearance of your graph.See 347 | https://github.com/sverweij/dependency-cruiser/blob/main/doc/options-reference.md#reporteroptions 348 | for details and some examples. If you don't specify a theme 349 | dependency-cruiser falls back to a built-in one. 350 | */ 351 | // theme: { 352 | // graph: { 353 | // /* splines: "ortho" gives straight lines, but is slow on big graphs 354 | // splines: "true" gives bezier curves (fast, not as nice as ortho) 355 | // */ 356 | // splines: "true" 357 | // }, 358 | // } 359 | }, 360 | archi: { 361 | /* pattern of modules that can be consolidated in the high level 362 | graphical dependency graph. If you use the high level graphical 363 | dependency graph reporter (`archi`) you probably want to tweak 364 | this collapsePattern to your situation. 365 | */ 366 | collapsePattern: '^(?:packages|src|lib(s?)|app(s?)|bin|test(s?)|spec(s?))/[^/]+|node_modules/(?:@[^/]+/[^/]+|[^/]+)', 367 | 368 | /* Options to tweak the appearance of your graph. If you don't specify a 369 | theme for 'archi' dependency-cruiser will use the one specified in the 370 | dot section above and otherwise use the default one. 371 | */ 372 | // theme: { }, 373 | }, 374 | "text": { 375 | "highlightFocused": true 376 | }, 377 | } 378 | } 379 | }; 380 | // generated: dependency-cruiser@16.3.3 on 2024-06-23T18:11:29.143Z 381 | -------------------------------------------------------------------------------- /examples/solidjs-with-layout/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist -------------------------------------------------------------------------------- /examples/solidjs-with-layout/README.md: -------------------------------------------------------------------------------- 1 | ## solidjs-with-layout 2 | 3 | This example shows how to work with layout (or layouts). Split layout to dumb component (with markup) and smart component for widget compositions. Dumb layout can be placed in `@shared/ui/Layout`, smart - `@app/layouts/BaseLayout.tsx`. See [`@app/layouts/baseLayout.tsx`](./src/app/layouts/BaseLayout.tsx) and [`@shared/ui/Layout/*`](./src/shared/ui/Layout) in code. 4 | 5 | ![Dependency Graph](./dependency-graph-preview.svg) 6 | 7 | ### Live preview 8 | 9 | Preview live with [StackBlitz](https://stackblitz.com/github/feature-sliced/examples/tree/master/examples/solidjs-with-layout?file=README.md). 10 | 11 | ### How to use 12 | 13 | Install deps and then run with npm, Yarn or pnpm to startup the example: 14 | 15 | ```bash 16 | npm run dev 17 | yarn dev 18 | pnpm dev 19 | ``` 20 | -------------------------------------------------------------------------------- /examples/solidjs-with-layout/dependency-graph-preview.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | dependency-cruiser output 11 | 12 | 13 | cluster_src 14 | 15 | src 16 | 17 | 18 | cluster_src/app 19 | 20 | app 21 | 22 | 23 | cluster_src/app/layouts 24 | 25 | layouts 26 | 27 | 28 | cluster_src/pages 29 | 30 | pages 31 | 32 | 33 | cluster_src/shared 34 | 35 | shared 36 | 37 | 38 | cluster_src/shared/ui 39 | 40 | ui 41 | 42 | 43 | cluster_src/shared/ui/Layout 44 | 45 | Layout 46 | 47 | 48 | cluster_src/widgets 49 | 50 | widgets 51 | 52 | 53 | cluster_src/widgets/header 54 | 55 | header 56 | 57 | 58 | cluster_src/widgets/header/ui 59 | 60 | ui 61 | 62 | 63 | 64 | src/app/App.tsx 65 | 66 | 67 | App.tsx 68 | 69 | 70 | 71 | 72 | 73 | src/app/layouts/index.ts 74 | 75 | 76 | index.ts 77 | 78 | 79 | 80 | 81 | 82 | src/app/App.tsx->src/app/layouts/index.ts 83 | 84 | 85 | 86 | 87 | 88 | src/pages/404.tsx 89 | 90 | 91 | 404.tsx 92 | 93 | 94 | 95 | 96 | 97 | src/app/App.tsx->src/pages/404.tsx 98 | 99 | 100 | 101 | 102 | 103 | src/pages/about.tsx 104 | 105 | 106 | about.tsx 107 | 108 | 109 | 110 | 111 | 112 | src/app/App.tsx->src/pages/about.tsx 113 | 114 | 115 | 116 | 117 | 118 | src/pages/home.tsx 119 | 120 | 121 | home.tsx 122 | 123 | 124 | 125 | 126 | 127 | src/app/App.tsx->src/pages/home.tsx 128 | 129 | 130 | 131 | 132 | 133 | src/app/layouts/BaseLayout.tsx 134 | 135 | 136 | BaseLayout.tsx 137 | 138 | 139 | 140 | 141 | 142 | src/app/layouts/index.ts->src/app/layouts/BaseLayout.tsx 143 | 144 | 145 | 146 | 147 | 148 | src/app/entry.css 149 | 150 | 151 | entry.css 152 | 153 | 154 | 155 | 156 | 157 | src/app/entry.tsx 158 | 159 | 160 | entry.tsx 161 | 162 | 163 | 164 | 165 | 166 | src/app/entry.tsx->src/app/App.tsx 167 | 168 | 169 | 170 | 171 | 172 | src/app/entry.tsx->src/app/entry.css 173 | 174 | 175 | 176 | 177 | 178 | src/shared/ui/index.ts 179 | 180 | 181 | index.ts 182 | 183 | 184 | 185 | 186 | 187 | src/app/layouts/BaseLayout.tsx->src/shared/ui/index.ts 188 | 189 | 190 | 191 | 192 | 193 | src/widgets/header/index.ts 194 | 195 | 196 | index.ts 197 | 198 | 199 | 200 | 201 | 202 | src/app/layouts/BaseLayout.tsx->src/widgets/header/index.ts 203 | 204 | 205 | 206 | 207 | 208 | src/shared/ui/Layout/Layout.tsx 209 | 210 | 211 | Layout.tsx 212 | 213 | 214 | 215 | 216 | 217 | src/shared/ui/index.ts->src/shared/ui/Layout/Layout.tsx 218 | 219 | 220 | 221 | 222 | 223 | src/widgets/header/ui/Header.tsx 224 | 225 | 226 | Header.tsx 227 | 228 | 229 | 230 | 231 | 232 | src/widgets/header/index.ts->src/widgets/header/ui/Header.tsx 233 | 234 | 235 | 236 | 237 | 238 | src/shared/ui/Layout/Layout.module.css 239 | 240 | 241 | Layout.module.css 242 | 243 | 244 | 245 | 246 | 247 | src/shared/ui/Layout/Layout.tsx->src/shared/ui/Layout/Layout.module.css 248 | 249 | 250 | 251 | 252 | 253 | src/widgets/header/ui/Header.module.css 254 | 255 | 256 | Header.module.css 257 | 258 | 259 | 260 | 261 | 262 | src/widgets/header/ui/Header.tsx->src/widgets/header/ui/Header.module.css 263 | 264 | 265 | 266 | 267 | 268 | -------------------------------------------------------------------------------- /examples/solidjs-with-layout/eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import antfu from '@antfu/eslint-config' 2 | 3 | export default antfu({ 4 | solid: true, 5 | formatters: { 6 | css: true, 7 | html: true, 8 | }, 9 | }, { 10 | rules: { 11 | '@typescript-eslint/consistent-type-definitions': ['error', 'type'], 12 | }, 13 | }) 14 | -------------------------------------------------------------------------------- /examples/solidjs-with-layout/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | solidjs-with-layout 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /examples/solidjs-with-layout/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "solidjs-with-layout", 3 | "private": true, 4 | "license": "MIT", 5 | "scripts": { 6 | "start": "vite", 7 | "dev": "vite", 8 | "build": "vite build", 9 | "serve": "vite preview", 10 | "lint": "eslint .", 11 | "lint:fix": "eslint . --fix", 12 | "lint:fsd": "steiger ./src", 13 | "dep-cruiser:preview": "npx depcruise src --include-only \"^src\" --output-type dot | dot -T svg > dependency-graph-preview.svg" 14 | }, 15 | "dependencies": { 16 | "@solidjs/meta": "^0.29.4", 17 | "@solidjs/router": "^0.13.6", 18 | "solid-js": "^1.8.11" 19 | }, 20 | "devDependencies": { 21 | "@antfu/eslint-config": "^2.21.1", 22 | "dependency-cruiser": "^16.3.3", 23 | "eslint": "^9.5.0", 24 | "eslint-plugin-format": "^0.1.2", 25 | "eslint-plugin-solid": "^0.14.0", 26 | "solid-devtools": "^0.29.2", 27 | "steiger": "^0.1.2", 28 | "typescript": "^5.3.3", 29 | "vite": "^5.0.11", 30 | "vite-plugin-solid": "^2.8.2", 31 | "vite-tsconfig-paths": "^4.3.2" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /examples/solidjs-with-layout/pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '6.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | dependencies: 8 | solid-js: 9 | specifier: ^1.8.11 10 | version: 1.8.11 11 | 12 | devDependencies: 13 | solid-devtools: 14 | specifier: ^0.29.2 15 | version: 0.29.2(solid-js@1.8.11)(vite@5.0.11) 16 | typescript: 17 | specifier: ^5.3.3 18 | version: 5.3.3 19 | vite: 20 | specifier: ^5.0.11 21 | version: 5.0.11 22 | vite-plugin-solid: 23 | specifier: ^2.8.2 24 | version: 2.8.2(solid-js@1.8.11)(vite@5.0.11) 25 | 26 | packages: 27 | 28 | /@ampproject/remapping@2.2.1: 29 | resolution: {integrity: sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==} 30 | engines: {node: '>=6.0.0'} 31 | dependencies: 32 | '@jridgewell/gen-mapping': 0.3.3 33 | '@jridgewell/trace-mapping': 0.3.18 34 | dev: true 35 | 36 | /@babel/code-frame@7.23.5: 37 | resolution: {integrity: sha512-CgH3s1a96LipHCmSUmYFPwY7MNx8C3avkq7i4Wl3cfa662ldtUe4VM1TPXX70pfmrlWTb6jLqTYrZyT2ZTJBgA==} 38 | engines: {node: '>=6.9.0'} 39 | dependencies: 40 | '@babel/highlight': 7.23.4 41 | chalk: 2.4.2 42 | dev: true 43 | 44 | /@babel/compat-data@7.23.5: 45 | resolution: {integrity: sha512-uU27kfDRlhfKl+w1U6vp16IuvSLtjAxdArVXPa9BvLkrr7CYIsxH5adpHObeAGY/41+syctUWOZ140a2Rvkgjw==} 46 | engines: {node: '>=6.9.0'} 47 | dev: true 48 | 49 | /@babel/core@7.23.7: 50 | resolution: {integrity: sha512-+UpDgowcmqe36d4NwqvKsyPMlOLNGMsfMmQ5WGCu+siCe3t3dfe9njrzGfdN4qq+bcNUt0+Vw6haRxBOycs4dw==} 51 | engines: {node: '>=6.9.0'} 52 | dependencies: 53 | '@ampproject/remapping': 2.2.1 54 | '@babel/code-frame': 7.23.5 55 | '@babel/generator': 7.23.6 56 | '@babel/helper-compilation-targets': 7.23.6 57 | '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.7) 58 | '@babel/helpers': 7.23.8 59 | '@babel/parser': 7.23.6 60 | '@babel/template': 7.22.15 61 | '@babel/traverse': 7.23.7 62 | '@babel/types': 7.23.6 63 | convert-source-map: 2.0.0 64 | debug: 4.3.4 65 | gensync: 1.0.0-beta.2 66 | json5: 2.2.3 67 | semver: 6.3.1 68 | transitivePeerDependencies: 69 | - supports-color 70 | dev: true 71 | 72 | /@babel/generator@7.23.6: 73 | resolution: {integrity: sha512-qrSfCYxYQB5owCmGLbl8XRpX1ytXlpueOb0N0UmQwA073KZxejgQTzAmJezxvpwQD9uGtK2shHdi55QT+MbjIw==} 74 | engines: {node: '>=6.9.0'} 75 | dependencies: 76 | '@babel/types': 7.23.6 77 | '@jridgewell/gen-mapping': 0.3.3 78 | '@jridgewell/trace-mapping': 0.3.18 79 | jsesc: 2.5.2 80 | dev: true 81 | 82 | /@babel/helper-annotate-as-pure@7.22.5: 83 | resolution: {integrity: sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==} 84 | engines: {node: '>=6.9.0'} 85 | dependencies: 86 | '@babel/types': 7.22.5 87 | dev: true 88 | 89 | /@babel/helper-compilation-targets@7.23.6: 90 | resolution: {integrity: sha512-9JB548GZoQVmzrFgp8o7KxdgkTGm6xs9DW0o/Pim72UDjzr5ObUQ6ZzYPqA+g9OTS2bBQoctLJrky0RDCAWRgQ==} 91 | engines: {node: '>=6.9.0'} 92 | dependencies: 93 | '@babel/compat-data': 7.23.5 94 | '@babel/helper-validator-option': 7.23.5 95 | browserslist: 4.22.2 96 | lru-cache: 5.1.1 97 | semver: 6.3.1 98 | dev: true 99 | 100 | /@babel/helper-create-class-features-plugin@7.23.7(@babel/core@7.23.7): 101 | resolution: {integrity: sha512-xCoqR/8+BoNnXOY7RVSgv6X+o7pmT5q1d+gGcRlXYkI+9B31glE4jeejhKVpA04O1AtzOt7OSQ6VYKP5FcRl9g==} 102 | engines: {node: '>=6.9.0'} 103 | peerDependencies: 104 | '@babel/core': ^7.0.0 105 | dependencies: 106 | '@babel/core': 7.23.7 107 | '@babel/helper-annotate-as-pure': 7.22.5 108 | '@babel/helper-environment-visitor': 7.22.20 109 | '@babel/helper-function-name': 7.23.0 110 | '@babel/helper-member-expression-to-functions': 7.23.0 111 | '@babel/helper-optimise-call-expression': 7.22.5 112 | '@babel/helper-replace-supers': 7.22.20(@babel/core@7.23.7) 113 | '@babel/helper-skip-transparent-expression-wrappers': 7.22.5 114 | '@babel/helper-split-export-declaration': 7.22.6 115 | semver: 6.3.1 116 | dev: true 117 | 118 | /@babel/helper-environment-visitor@7.22.20: 119 | resolution: {integrity: sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==} 120 | engines: {node: '>=6.9.0'} 121 | dev: true 122 | 123 | /@babel/helper-function-name@7.23.0: 124 | resolution: {integrity: sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==} 125 | engines: {node: '>=6.9.0'} 126 | dependencies: 127 | '@babel/template': 7.22.15 128 | '@babel/types': 7.23.6 129 | dev: true 130 | 131 | /@babel/helper-hoist-variables@7.22.5: 132 | resolution: {integrity: sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==} 133 | engines: {node: '>=6.9.0'} 134 | dependencies: 135 | '@babel/types': 7.23.6 136 | dev: true 137 | 138 | /@babel/helper-member-expression-to-functions@7.23.0: 139 | resolution: {integrity: sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==} 140 | engines: {node: '>=6.9.0'} 141 | dependencies: 142 | '@babel/types': 7.23.6 143 | dev: true 144 | 145 | /@babel/helper-module-imports@7.18.6: 146 | resolution: {integrity: sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==} 147 | engines: {node: '>=6.9.0'} 148 | dependencies: 149 | '@babel/types': 7.22.5 150 | dev: true 151 | 152 | /@babel/helper-module-imports@7.22.15: 153 | resolution: {integrity: sha512-0pYVBnDKZO2fnSPCrgM/6WMc7eS20Fbok+0r88fp+YtWVLZrp4CkafFGIp+W0VKw4a22sgebPT99y+FDNMdP4w==} 154 | engines: {node: '>=6.9.0'} 155 | dependencies: 156 | '@babel/types': 7.23.6 157 | dev: true 158 | 159 | /@babel/helper-module-transforms@7.23.3(@babel/core@7.23.7): 160 | resolution: {integrity: sha512-7bBs4ED9OmswdfDzpz4MpWgSrV7FXlc3zIagvLFjS5H+Mk7Snr21vQ6QwrsoCGMfNC4e4LQPdoULEt4ykz0SRQ==} 161 | engines: {node: '>=6.9.0'} 162 | peerDependencies: 163 | '@babel/core': ^7.0.0 164 | dependencies: 165 | '@babel/core': 7.23.7 166 | '@babel/helper-environment-visitor': 7.22.20 167 | '@babel/helper-module-imports': 7.22.15 168 | '@babel/helper-simple-access': 7.22.5 169 | '@babel/helper-split-export-declaration': 7.22.6 170 | '@babel/helper-validator-identifier': 7.22.20 171 | dev: true 172 | 173 | /@babel/helper-optimise-call-expression@7.22.5: 174 | resolution: {integrity: sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==} 175 | engines: {node: '>=6.9.0'} 176 | dependencies: 177 | '@babel/types': 7.22.5 178 | dev: true 179 | 180 | /@babel/helper-plugin-utils@7.22.5: 181 | resolution: {integrity: sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==} 182 | engines: {node: '>=6.9.0'} 183 | dev: true 184 | 185 | /@babel/helper-replace-supers@7.22.20(@babel/core@7.23.7): 186 | resolution: {integrity: sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==} 187 | engines: {node: '>=6.9.0'} 188 | peerDependencies: 189 | '@babel/core': ^7.0.0 190 | dependencies: 191 | '@babel/core': 7.23.7 192 | '@babel/helper-environment-visitor': 7.22.20 193 | '@babel/helper-member-expression-to-functions': 7.23.0 194 | '@babel/helper-optimise-call-expression': 7.22.5 195 | dev: true 196 | 197 | /@babel/helper-simple-access@7.22.5: 198 | resolution: {integrity: sha512-n0H99E/K+Bika3++WNL17POvo4rKWZ7lZEp1Q+fStVbUi8nxPQEBOlTmCOxW/0JsS56SKKQ+ojAe2pHKJHN35w==} 199 | engines: {node: '>=6.9.0'} 200 | dependencies: 201 | '@babel/types': 7.22.5 202 | dev: true 203 | 204 | /@babel/helper-skip-transparent-expression-wrappers@7.22.5: 205 | resolution: {integrity: sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==} 206 | engines: {node: '>=6.9.0'} 207 | dependencies: 208 | '@babel/types': 7.22.5 209 | dev: true 210 | 211 | /@babel/helper-split-export-declaration@7.22.6: 212 | resolution: {integrity: sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==} 213 | engines: {node: '>=6.9.0'} 214 | dependencies: 215 | '@babel/types': 7.22.5 216 | dev: true 217 | 218 | /@babel/helper-string-parser@7.22.5: 219 | resolution: {integrity: sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==} 220 | engines: {node: '>=6.9.0'} 221 | dev: true 222 | 223 | /@babel/helper-string-parser@7.23.4: 224 | resolution: {integrity: sha512-803gmbQdqwdf4olxrX4AJyFBV/RTr3rSmOj0rKwesmzlfhYNDEs+/iOcznzpNWlJlIlTJC2QfPFcHB6DlzdVLQ==} 225 | engines: {node: '>=6.9.0'} 226 | dev: true 227 | 228 | /@babel/helper-validator-identifier@7.22.20: 229 | resolution: {integrity: sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==} 230 | engines: {node: '>=6.9.0'} 231 | dev: true 232 | 233 | /@babel/helper-validator-identifier@7.22.5: 234 | resolution: {integrity: sha512-aJXu+6lErq8ltp+JhkJUfk1MTGyuA4v7f3pA+BJ5HLfNC6nAQ0Cpi9uOquUj8Hehg0aUiHzWQbOVJGao6ztBAQ==} 235 | engines: {node: '>=6.9.0'} 236 | dev: true 237 | 238 | /@babel/helper-validator-option@7.23.5: 239 | resolution: {integrity: sha512-85ttAOMLsr53VgXkTbkx8oA6YTfT4q7/HzXSLEYmjcSTJPMPQtvq1BD79Byep5xMUYbGRzEpDsjUf3dyp54IKw==} 240 | engines: {node: '>=6.9.0'} 241 | dev: true 242 | 243 | /@babel/helpers@7.23.8: 244 | resolution: {integrity: sha512-KDqYz4PiOWvDFrdHLPhKtCThtIcKVy6avWD2oG4GEvyQ+XDZwHD4YQd+H2vNMnq2rkdxsDkU82T+Vk8U/WXHRQ==} 245 | engines: {node: '>=6.9.0'} 246 | dependencies: 247 | '@babel/template': 7.22.15 248 | '@babel/traverse': 7.23.7 249 | '@babel/types': 7.23.6 250 | transitivePeerDependencies: 251 | - supports-color 252 | dev: true 253 | 254 | /@babel/highlight@7.23.4: 255 | resolution: {integrity: sha512-acGdbYSfp2WheJoJm/EBBBLh/ID8KDc64ISZ9DYtBmC8/Q204PZJLHyzeB5qMzJ5trcOkybd78M4x2KWsUq++A==} 256 | engines: {node: '>=6.9.0'} 257 | dependencies: 258 | '@babel/helper-validator-identifier': 7.22.20 259 | chalk: 2.4.2 260 | js-tokens: 4.0.0 261 | dev: true 262 | 263 | /@babel/parser@7.22.5: 264 | resolution: {integrity: sha512-DFZMC9LJUG9PLOclRC32G63UXwzqS2koQC8dkx+PLdmt1xSePYpbT/NbsrJy8Q/muXz7o/h/d4A7Fuyixm559Q==} 265 | engines: {node: '>=6.0.0'} 266 | hasBin: true 267 | dependencies: 268 | '@babel/types': 7.22.5 269 | dev: true 270 | 271 | /@babel/parser@7.23.6: 272 | resolution: {integrity: sha512-Z2uID7YJ7oNvAI20O9X0bblw7Qqs8Q2hFy0R9tAfnfLkp5MW0UH9eUvnDSnFwKZ0AvgS1ucqR4KzvVHgnke1VQ==} 273 | engines: {node: '>=6.0.0'} 274 | hasBin: true 275 | dependencies: 276 | '@babel/types': 7.23.6 277 | dev: true 278 | 279 | /@babel/plugin-syntax-jsx@7.22.5(@babel/core@7.23.7): 280 | resolution: {integrity: sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==} 281 | engines: {node: '>=6.9.0'} 282 | peerDependencies: 283 | '@babel/core': ^7.0.0-0 284 | dependencies: 285 | '@babel/core': 7.23.7 286 | '@babel/helper-plugin-utils': 7.22.5 287 | dev: true 288 | 289 | /@babel/plugin-syntax-jsx@7.23.3(@babel/core@7.23.7): 290 | resolution: {integrity: sha512-EB2MELswq55OHUoRZLGg/zC7QWUKfNLpE57m/S2yr1uEneIgsTgrSzXP3NXEsMkVn76OlaVVnzN+ugObuYGwhg==} 291 | engines: {node: '>=6.9.0'} 292 | peerDependencies: 293 | '@babel/core': ^7.0.0-0 294 | dependencies: 295 | '@babel/core': 7.23.7 296 | '@babel/helper-plugin-utils': 7.22.5 297 | dev: true 298 | 299 | /@babel/plugin-syntax-typescript@7.23.3(@babel/core@7.23.7): 300 | resolution: {integrity: sha512-9EiNjVJOMwCO+43TqoTrgQ8jMwcAd0sWyXi9RPfIsLTj4R2MADDDQXELhffaUx/uJv2AYcxBgPwH6j4TIA4ytQ==} 301 | engines: {node: '>=6.9.0'} 302 | peerDependencies: 303 | '@babel/core': ^7.0.0-0 304 | dependencies: 305 | '@babel/core': 7.23.7 306 | '@babel/helper-plugin-utils': 7.22.5 307 | dev: true 308 | 309 | /@babel/plugin-transform-modules-commonjs@7.23.3(@babel/core@7.23.7): 310 | resolution: {integrity: sha512-aVS0F65LKsdNOtcz6FRCpE4OgsP2OFnW46qNxNIX9h3wuzaNcSQsJysuMwqSibC98HPrf2vCgtxKNwS0DAlgcA==} 311 | engines: {node: '>=6.9.0'} 312 | peerDependencies: 313 | '@babel/core': ^7.0.0-0 314 | dependencies: 315 | '@babel/core': 7.23.7 316 | '@babel/helper-module-transforms': 7.23.3(@babel/core@7.23.7) 317 | '@babel/helper-plugin-utils': 7.22.5 318 | '@babel/helper-simple-access': 7.22.5 319 | dev: true 320 | 321 | /@babel/plugin-transform-typescript@7.23.6(@babel/core@7.23.7): 322 | resolution: {integrity: sha512-6cBG5mBvUu4VUD04OHKnYzbuHNP8huDsD3EDqqpIpsswTDoqHCjLoHb6+QgsV1WsT2nipRqCPgxD3LXnEO7XfA==} 323 | engines: {node: '>=6.9.0'} 324 | peerDependencies: 325 | '@babel/core': ^7.0.0-0 326 | dependencies: 327 | '@babel/core': 7.23.7 328 | '@babel/helper-annotate-as-pure': 7.22.5 329 | '@babel/helper-create-class-features-plugin': 7.23.7(@babel/core@7.23.7) 330 | '@babel/helper-plugin-utils': 7.22.5 331 | '@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.23.7) 332 | dev: true 333 | 334 | /@babel/preset-typescript@7.23.3(@babel/core@7.23.7): 335 | resolution: {integrity: sha512-17oIGVlqz6CchO9RFYn5U6ZpWRZIngayYCtrPRSgANSwC2V1Jb+iP74nVxzzXJte8b8BYxrL1yY96xfhTBrNNQ==} 336 | engines: {node: '>=6.9.0'} 337 | peerDependencies: 338 | '@babel/core': ^7.0.0-0 339 | dependencies: 340 | '@babel/core': 7.23.7 341 | '@babel/helper-plugin-utils': 7.22.5 342 | '@babel/helper-validator-option': 7.23.5 343 | '@babel/plugin-syntax-jsx': 7.23.3(@babel/core@7.23.7) 344 | '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.23.7) 345 | '@babel/plugin-transform-typescript': 7.23.6(@babel/core@7.23.7) 346 | dev: true 347 | 348 | /@babel/template@7.22.15: 349 | resolution: {integrity: sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==} 350 | engines: {node: '>=6.9.0'} 351 | dependencies: 352 | '@babel/code-frame': 7.23.5 353 | '@babel/parser': 7.23.6 354 | '@babel/types': 7.23.6 355 | dev: true 356 | 357 | /@babel/traverse@7.23.7: 358 | resolution: {integrity: sha512-tY3mM8rH9jM0YHFGyfC0/xf+SB5eKUu7HPj7/k3fpi9dAlsMc5YbQvDi0Sh2QTPXqMhyaAtzAr807TIyfQrmyg==} 359 | engines: {node: '>=6.9.0'} 360 | dependencies: 361 | '@babel/code-frame': 7.23.5 362 | '@babel/generator': 7.23.6 363 | '@babel/helper-environment-visitor': 7.22.20 364 | '@babel/helper-function-name': 7.23.0 365 | '@babel/helper-hoist-variables': 7.22.5 366 | '@babel/helper-split-export-declaration': 7.22.6 367 | '@babel/parser': 7.23.6 368 | '@babel/types': 7.23.6 369 | debug: 4.3.4 370 | globals: 11.12.0 371 | transitivePeerDependencies: 372 | - supports-color 373 | dev: true 374 | 375 | /@babel/types@7.22.5: 376 | resolution: {integrity: sha512-zo3MIHGOkPOfoRXitsgHLjEXmlDaD/5KU1Uzuc9GNiZPhSqVxVRtxuPaSBZDsYZ9qV88AjtMtWW7ww98loJ9KA==} 377 | engines: {node: '>=6.9.0'} 378 | dependencies: 379 | '@babel/helper-string-parser': 7.22.5 380 | '@babel/helper-validator-identifier': 7.22.5 381 | to-fast-properties: 2.0.0 382 | dev: true 383 | 384 | /@babel/types@7.23.6: 385 | resolution: {integrity: sha512-+uarb83brBzPKN38NX1MkB6vb6+mwvR6amUulqAE7ccQw1pEl+bCia9TbdG1lsnFP7lZySvUn37CHyXQdfTwzg==} 386 | engines: {node: '>=6.9.0'} 387 | dependencies: 388 | '@babel/helper-string-parser': 7.23.4 389 | '@babel/helper-validator-identifier': 7.22.20 390 | to-fast-properties: 2.0.0 391 | dev: true 392 | 393 | /@esbuild/aix-ppc64@0.19.11: 394 | resolution: {integrity: sha512-FnzU0LyE3ySQk7UntJO4+qIiQgI7KoODnZg5xzXIrFJlKd2P2gwHsHY4927xj9y5PJmJSzULiUCWmv7iWnNa7g==} 395 | engines: {node: '>=12'} 396 | cpu: [ppc64] 397 | os: [aix] 398 | requiresBuild: true 399 | dev: true 400 | optional: true 401 | 402 | /@esbuild/android-arm64@0.19.11: 403 | resolution: {integrity: sha512-aiu7K/5JnLj//KOnOfEZ0D90obUkRzDMyqd/wNAUQ34m4YUPVhRZpnqKV9uqDGxT7cToSDnIHsGooyIczu9T+Q==} 404 | engines: {node: '>=12'} 405 | cpu: [arm64] 406 | os: [android] 407 | requiresBuild: true 408 | dev: true 409 | optional: true 410 | 411 | /@esbuild/android-arm@0.19.11: 412 | resolution: {integrity: sha512-5OVapq0ClabvKvQ58Bws8+wkLCV+Rxg7tUVbo9xu034Nm536QTII4YzhaFriQ7rMrorfnFKUsArD2lqKbFY4vw==} 413 | engines: {node: '>=12'} 414 | cpu: [arm] 415 | os: [android] 416 | requiresBuild: true 417 | dev: true 418 | optional: true 419 | 420 | /@esbuild/android-x64@0.19.11: 421 | resolution: {integrity: sha512-eccxjlfGw43WYoY9QgB82SgGgDbibcqyDTlk3l3C0jOVHKxrjdc9CTwDUQd0vkvYg5um0OH+GpxYvp39r+IPOg==} 422 | engines: {node: '>=12'} 423 | cpu: [x64] 424 | os: [android] 425 | requiresBuild: true 426 | dev: true 427 | optional: true 428 | 429 | /@esbuild/darwin-arm64@0.19.11: 430 | resolution: {integrity: sha512-ETp87DRWuSt9KdDVkqSoKoLFHYTrkyz2+65fj9nfXsaV3bMhTCjtQfw3y+um88vGRKRiF7erPrh/ZuIdLUIVxQ==} 431 | engines: {node: '>=12'} 432 | cpu: [arm64] 433 | os: [darwin] 434 | requiresBuild: true 435 | dev: true 436 | optional: true 437 | 438 | /@esbuild/darwin-x64@0.19.11: 439 | resolution: {integrity: sha512-fkFUiS6IUK9WYUO/+22omwetaSNl5/A8giXvQlcinLIjVkxwTLSktbF5f/kJMftM2MJp9+fXqZ5ezS7+SALp4g==} 440 | engines: {node: '>=12'} 441 | cpu: [x64] 442 | os: [darwin] 443 | requiresBuild: true 444 | dev: true 445 | optional: true 446 | 447 | /@esbuild/freebsd-arm64@0.19.11: 448 | resolution: {integrity: sha512-lhoSp5K6bxKRNdXUtHoNc5HhbXVCS8V0iZmDvyWvYq9S5WSfTIHU2UGjcGt7UeS6iEYp9eeymIl5mJBn0yiuxA==} 449 | engines: {node: '>=12'} 450 | cpu: [arm64] 451 | os: [freebsd] 452 | requiresBuild: true 453 | dev: true 454 | optional: true 455 | 456 | /@esbuild/freebsd-x64@0.19.11: 457 | resolution: {integrity: sha512-JkUqn44AffGXitVI6/AbQdoYAq0TEullFdqcMY/PCUZ36xJ9ZJRtQabzMA+Vi7r78+25ZIBosLTOKnUXBSi1Kw==} 458 | engines: {node: '>=12'} 459 | cpu: [x64] 460 | os: [freebsd] 461 | requiresBuild: true 462 | dev: true 463 | optional: true 464 | 465 | /@esbuild/linux-arm64@0.19.11: 466 | resolution: {integrity: sha512-LneLg3ypEeveBSMuoa0kwMpCGmpu8XQUh+mL8XXwoYZ6Be2qBnVtcDI5azSvh7vioMDhoJFZzp9GWp9IWpYoUg==} 467 | engines: {node: '>=12'} 468 | cpu: [arm64] 469 | os: [linux] 470 | requiresBuild: true 471 | dev: true 472 | optional: true 473 | 474 | /@esbuild/linux-arm@0.19.11: 475 | resolution: {integrity: sha512-3CRkr9+vCV2XJbjwgzjPtO8T0SZUmRZla+UL1jw+XqHZPkPgZiyWvbDvl9rqAN8Zl7qJF0O/9ycMtjU67HN9/Q==} 476 | engines: {node: '>=12'} 477 | cpu: [arm] 478 | os: [linux] 479 | requiresBuild: true 480 | dev: true 481 | optional: true 482 | 483 | /@esbuild/linux-ia32@0.19.11: 484 | resolution: {integrity: sha512-caHy++CsD8Bgq2V5CodbJjFPEiDPq8JJmBdeyZ8GWVQMjRD0sU548nNdwPNvKjVpamYYVL40AORekgfIubwHoA==} 485 | engines: {node: '>=12'} 486 | cpu: [ia32] 487 | os: [linux] 488 | requiresBuild: true 489 | dev: true 490 | optional: true 491 | 492 | /@esbuild/linux-loong64@0.19.11: 493 | resolution: {integrity: sha512-ppZSSLVpPrwHccvC6nQVZaSHlFsvCQyjnvirnVjbKSHuE5N24Yl8F3UwYUUR1UEPaFObGD2tSvVKbvR+uT1Nrg==} 494 | engines: {node: '>=12'} 495 | cpu: [loong64] 496 | os: [linux] 497 | requiresBuild: true 498 | dev: true 499 | optional: true 500 | 501 | /@esbuild/linux-mips64el@0.19.11: 502 | resolution: {integrity: sha512-B5x9j0OgjG+v1dF2DkH34lr+7Gmv0kzX6/V0afF41FkPMMqaQ77pH7CrhWeR22aEeHKaeZVtZ6yFwlxOKPVFyg==} 503 | engines: {node: '>=12'} 504 | cpu: [mips64el] 505 | os: [linux] 506 | requiresBuild: true 507 | dev: true 508 | optional: true 509 | 510 | /@esbuild/linux-ppc64@0.19.11: 511 | resolution: {integrity: sha512-MHrZYLeCG8vXblMetWyttkdVRjQlQUb/oMgBNurVEnhj4YWOr4G5lmBfZjHYQHHN0g6yDmCAQRR8MUHldvvRDA==} 512 | engines: {node: '>=12'} 513 | cpu: [ppc64] 514 | os: [linux] 515 | requiresBuild: true 516 | dev: true 517 | optional: true 518 | 519 | /@esbuild/linux-riscv64@0.19.11: 520 | resolution: {integrity: sha512-f3DY++t94uVg141dozDu4CCUkYW+09rWtaWfnb3bqe4w5NqmZd6nPVBm+qbz7WaHZCoqXqHz5p6CM6qv3qnSSQ==} 521 | engines: {node: '>=12'} 522 | cpu: [riscv64] 523 | os: [linux] 524 | requiresBuild: true 525 | dev: true 526 | optional: true 527 | 528 | /@esbuild/linux-s390x@0.19.11: 529 | resolution: {integrity: sha512-A5xdUoyWJHMMlcSMcPGVLzYzpcY8QP1RtYzX5/bS4dvjBGVxdhuiYyFwp7z74ocV7WDc0n1harxmpq2ePOjI0Q==} 530 | engines: {node: '>=12'} 531 | cpu: [s390x] 532 | os: [linux] 533 | requiresBuild: true 534 | dev: true 535 | optional: true 536 | 537 | /@esbuild/linux-x64@0.19.11: 538 | resolution: {integrity: sha512-grbyMlVCvJSfxFQUndw5mCtWs5LO1gUlwP4CDi4iJBbVpZcqLVT29FxgGuBJGSzyOxotFG4LoO5X+M1350zmPA==} 539 | engines: {node: '>=12'} 540 | cpu: [x64] 541 | os: [linux] 542 | requiresBuild: true 543 | dev: true 544 | optional: true 545 | 546 | /@esbuild/netbsd-x64@0.19.11: 547 | resolution: {integrity: sha512-13jvrQZJc3P230OhU8xgwUnDeuC/9egsjTkXN49b3GcS5BKvJqZn86aGM8W9pd14Kd+u7HuFBMVtrNGhh6fHEQ==} 548 | engines: {node: '>=12'} 549 | cpu: [x64] 550 | os: [netbsd] 551 | requiresBuild: true 552 | dev: true 553 | optional: true 554 | 555 | /@esbuild/openbsd-x64@0.19.11: 556 | resolution: {integrity: sha512-ysyOGZuTp6SNKPE11INDUeFVVQFrhcNDVUgSQVDzqsqX38DjhPEPATpid04LCoUr2WXhQTEZ8ct/EgJCUDpyNw==} 557 | engines: {node: '>=12'} 558 | cpu: [x64] 559 | os: [openbsd] 560 | requiresBuild: true 561 | dev: true 562 | optional: true 563 | 564 | /@esbuild/sunos-x64@0.19.11: 565 | resolution: {integrity: sha512-Hf+Sad9nVwvtxy4DXCZQqLpgmRTQqyFyhT3bZ4F2XlJCjxGmRFF0Shwn9rzhOYRB61w9VMXUkxlBy56dk9JJiQ==} 566 | engines: {node: '>=12'} 567 | cpu: [x64] 568 | os: [sunos] 569 | requiresBuild: true 570 | dev: true 571 | optional: true 572 | 573 | /@esbuild/win32-arm64@0.19.11: 574 | resolution: {integrity: sha512-0P58Sbi0LctOMOQbpEOvOL44Ne0sqbS0XWHMvvrg6NE5jQ1xguCSSw9jQeUk2lfrXYsKDdOe6K+oZiwKPilYPQ==} 575 | engines: {node: '>=12'} 576 | cpu: [arm64] 577 | os: [win32] 578 | requiresBuild: true 579 | dev: true 580 | optional: true 581 | 582 | /@esbuild/win32-ia32@0.19.11: 583 | resolution: {integrity: sha512-6YOrWS+sDJDmshdBIQU+Uoyh7pQKrdykdefC1avn76ss5c+RN6gut3LZA4E2cH5xUEp5/cA0+YxRaVtRAb0xBg==} 584 | engines: {node: '>=12'} 585 | cpu: [ia32] 586 | os: [win32] 587 | requiresBuild: true 588 | dev: true 589 | optional: true 590 | 591 | /@esbuild/win32-x64@0.19.11: 592 | resolution: {integrity: sha512-vfkhltrjCAb603XaFhqhAF4LGDi2M4OrCRrFusyQ+iTLQ/o60QQXxc9cZC/FFpihBI9N1Grn6SMKVJ4KP7Fuiw==} 593 | engines: {node: '>=12'} 594 | cpu: [x64] 595 | os: [win32] 596 | requiresBuild: true 597 | dev: true 598 | optional: true 599 | 600 | /@jridgewell/gen-mapping@0.3.3: 601 | resolution: {integrity: sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==} 602 | engines: {node: '>=6.0.0'} 603 | dependencies: 604 | '@jridgewell/set-array': 1.1.2 605 | '@jridgewell/sourcemap-codec': 1.4.15 606 | '@jridgewell/trace-mapping': 0.3.18 607 | dev: true 608 | 609 | /@jridgewell/resolve-uri@3.1.0: 610 | resolution: {integrity: sha512-F2msla3tad+Mfht5cJq7LSXcdudKTWCVYUgw6pLFOOHSTtZlj6SWNYAp+AhuqLmWdBO2X5hPrLcu8cVP8fy28w==} 611 | engines: {node: '>=6.0.0'} 612 | dev: true 613 | 614 | /@jridgewell/set-array@1.1.2: 615 | resolution: {integrity: sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==} 616 | engines: {node: '>=6.0.0'} 617 | dev: true 618 | 619 | /@jridgewell/sourcemap-codec@1.4.14: 620 | resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} 621 | dev: true 622 | 623 | /@jridgewell/sourcemap-codec@1.4.15: 624 | resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} 625 | dev: true 626 | 627 | /@jridgewell/trace-mapping@0.3.18: 628 | resolution: {integrity: sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==} 629 | dependencies: 630 | '@jridgewell/resolve-uri': 3.1.0 631 | '@jridgewell/sourcemap-codec': 1.4.14 632 | dev: true 633 | 634 | /@nothing-but/utils@0.12.1: 635 | resolution: {integrity: sha512-1qZU1Q5El0IjE7JT/ucvJNzdr2hL3W8Rm27xNf1p6gb3Nw8pGnZmxp6/GEW9h+I1k1cICxXNq25hBwknTQ7yhg==} 636 | dev: true 637 | 638 | /@rollup/rollup-android-arm-eabi@4.9.5: 639 | resolution: {integrity: sha512-idWaG8xeSRCfRq9KpRysDHJ/rEHBEXcHuJ82XY0yYFIWnLMjZv9vF/7DOq8djQ2n3Lk6+3qfSH8AqlmHlmi1MA==} 640 | cpu: [arm] 641 | os: [android] 642 | requiresBuild: true 643 | dev: true 644 | optional: true 645 | 646 | /@rollup/rollup-android-arm64@4.9.5: 647 | resolution: {integrity: sha512-f14d7uhAMtsCGjAYwZGv6TwuS3IFaM4ZnGMUn3aCBgkcHAYErhV1Ad97WzBvS2o0aaDv4mVz+syiN0ElMyfBPg==} 648 | cpu: [arm64] 649 | os: [android] 650 | requiresBuild: true 651 | dev: true 652 | optional: true 653 | 654 | /@rollup/rollup-darwin-arm64@4.9.5: 655 | resolution: {integrity: sha512-ndoXeLx455FffL68OIUrVr89Xu1WLzAG4n65R8roDlCoYiQcGGg6MALvs2Ap9zs7AHg8mpHtMpwC8jBBjZrT/w==} 656 | cpu: [arm64] 657 | os: [darwin] 658 | requiresBuild: true 659 | dev: true 660 | optional: true 661 | 662 | /@rollup/rollup-darwin-x64@4.9.5: 663 | resolution: {integrity: sha512-UmElV1OY2m/1KEEqTlIjieKfVwRg0Zwg4PLgNf0s3glAHXBN99KLpw5A5lrSYCa1Kp63czTpVll2MAqbZYIHoA==} 664 | cpu: [x64] 665 | os: [darwin] 666 | requiresBuild: true 667 | dev: true 668 | optional: true 669 | 670 | /@rollup/rollup-linux-arm-gnueabihf@4.9.5: 671 | resolution: {integrity: sha512-Q0LcU61v92tQB6ae+udZvOyZ0wfpGojtAKrrpAaIqmJ7+psq4cMIhT/9lfV6UQIpeItnq/2QDROhNLo00lOD1g==} 672 | cpu: [arm] 673 | os: [linux] 674 | requiresBuild: true 675 | dev: true 676 | optional: true 677 | 678 | /@rollup/rollup-linux-arm64-gnu@4.9.5: 679 | resolution: {integrity: sha512-dkRscpM+RrR2Ee3eOQmRWFjmV/payHEOrjyq1VZegRUa5OrZJ2MAxBNs05bZuY0YCtpqETDy1Ix4i/hRqX98cA==} 680 | cpu: [arm64] 681 | os: [linux] 682 | requiresBuild: true 683 | dev: true 684 | optional: true 685 | 686 | /@rollup/rollup-linux-arm64-musl@4.9.5: 687 | resolution: {integrity: sha512-QaKFVOzzST2xzY4MAmiDmURagWLFh+zZtttuEnuNn19AiZ0T3fhPyjPPGwLNdiDT82ZE91hnfJsUiDwF9DClIQ==} 688 | cpu: [arm64] 689 | os: [linux] 690 | requiresBuild: true 691 | dev: true 692 | optional: true 693 | 694 | /@rollup/rollup-linux-riscv64-gnu@4.9.5: 695 | resolution: {integrity: sha512-HeGqmRJuyVg6/X6MpE2ur7GbymBPS8Np0S/vQFHDmocfORT+Zt76qu+69NUoxXzGqVP1pzaY6QIi0FJWLC3OPA==} 696 | cpu: [riscv64] 697 | os: [linux] 698 | requiresBuild: true 699 | dev: true 700 | optional: true 701 | 702 | /@rollup/rollup-linux-x64-gnu@4.9.5: 703 | resolution: {integrity: sha512-Dq1bqBdLaZ1Gb/l2e5/+o3B18+8TI9ANlA1SkejZqDgdU/jK/ThYaMPMJpVMMXy2uRHvGKbkz9vheVGdq3cJfA==} 704 | cpu: [x64] 705 | os: [linux] 706 | requiresBuild: true 707 | dev: true 708 | optional: true 709 | 710 | /@rollup/rollup-linux-x64-musl@4.9.5: 711 | resolution: {integrity: sha512-ezyFUOwldYpj7AbkwyW9AJ203peub81CaAIVvckdkyH8EvhEIoKzaMFJj0G4qYJ5sw3BpqhFrsCc30t54HV8vg==} 712 | cpu: [x64] 713 | os: [linux] 714 | requiresBuild: true 715 | dev: true 716 | optional: true 717 | 718 | /@rollup/rollup-win32-arm64-msvc@4.9.5: 719 | resolution: {integrity: sha512-aHSsMnUw+0UETB0Hlv7B/ZHOGY5bQdwMKJSzGfDfvyhnpmVxLMGnQPGNE9wgqkLUs3+gbG1Qx02S2LLfJ5GaRQ==} 720 | cpu: [arm64] 721 | os: [win32] 722 | requiresBuild: true 723 | dev: true 724 | optional: true 725 | 726 | /@rollup/rollup-win32-ia32-msvc@4.9.5: 727 | resolution: {integrity: sha512-AiqiLkb9KSf7Lj/o1U3SEP9Zn+5NuVKgFdRIZkvd4N0+bYrTOovVd0+LmYCPQGbocT4kvFyK+LXCDiXPBF3fyA==} 728 | cpu: [ia32] 729 | os: [win32] 730 | requiresBuild: true 731 | dev: true 732 | optional: true 733 | 734 | /@rollup/rollup-win32-x64-msvc@4.9.5: 735 | resolution: {integrity: sha512-1q+mykKE3Vot1kaFJIDoUFv5TuW+QQVaf2FmTT9krg86pQrGStOSJJ0Zil7CFagyxDuouTepzt5Y5TVzyajOdQ==} 736 | cpu: [x64] 737 | os: [win32] 738 | requiresBuild: true 739 | dev: true 740 | optional: true 741 | 742 | /@solid-devtools/debugger@0.23.3(solid-js@1.8.11): 743 | resolution: {integrity: sha512-VrgswTjb2FyHxQJp5y5u7OaJ2k1R14LYlAOX/1rDZrGHWKdGYCaWHGzxI7C8AExtMP+LS+WOxy0uXMPQpoAD2g==} 744 | peerDependencies: 745 | solid-js: ^1.8.0 746 | dependencies: 747 | '@nothing-but/utils': 0.12.1 748 | '@solid-devtools/shared': 0.13.1(solid-js@1.8.11) 749 | '@solid-primitives/bounds': 0.0.118(solid-js@1.8.11) 750 | '@solid-primitives/cursor': 0.0.112(solid-js@1.8.11) 751 | '@solid-primitives/event-bus': 1.0.9(solid-js@1.8.11) 752 | '@solid-primitives/event-listener': 2.3.1(solid-js@1.8.11) 753 | '@solid-primitives/keyboard': 1.2.6(solid-js@1.8.11) 754 | '@solid-primitives/platform': 0.1.1(solid-js@1.8.11) 755 | '@solid-primitives/rootless': 1.4.3(solid-js@1.8.11) 756 | '@solid-primitives/scheduled': 1.4.2(solid-js@1.8.11) 757 | '@solid-primitives/static-store': 0.0.5(solid-js@1.8.11) 758 | '@solid-primitives/utils': 6.2.2(solid-js@1.8.11) 759 | solid-js: 1.8.11 760 | dev: true 761 | 762 | /@solid-devtools/shared@0.13.1(solid-js@1.8.11): 763 | resolution: {integrity: sha512-qaAcZF47FFr4alVQSy5ooLy7mMt4MMDxSHw52heY1oCut8yfXDrnLcYDONabfoin2WYIwsQpjYhryHgjtB0uDg==} 764 | peerDependencies: 765 | solid-js: ^1.8.0 766 | dependencies: 767 | '@solid-primitives/event-bus': 1.0.9(solid-js@1.8.11) 768 | '@solid-primitives/event-listener': 2.3.1(solid-js@1.8.11) 769 | '@solid-primitives/media': 2.2.6(solid-js@1.8.11) 770 | '@solid-primitives/refs': 1.0.6(solid-js@1.8.11) 771 | '@solid-primitives/rootless': 1.4.3(solid-js@1.8.11) 772 | '@solid-primitives/scheduled': 1.4.2(solid-js@1.8.11) 773 | '@solid-primitives/static-store': 0.0.5(solid-js@1.8.11) 774 | '@solid-primitives/styles': 0.0.111(solid-js@1.8.11) 775 | '@solid-primitives/utils': 6.2.2(solid-js@1.8.11) 776 | solid-js: 1.8.11 777 | dev: true 778 | 779 | /@solid-primitives/bounds@0.0.118(solid-js@1.8.11): 780 | resolution: {integrity: sha512-Qj42w8LlnhJ3r/t+t0c0vrdwIvvQMPgjEFGmLiwREaA85ojLbgL9lSBq2tKvljeLCvRVkgj10KEUf+vc99VCIg==} 781 | peerDependencies: 782 | solid-js: ^1.6.12 783 | dependencies: 784 | '@solid-primitives/event-listener': 2.3.1(solid-js@1.8.11) 785 | '@solid-primitives/resize-observer': 2.0.23(solid-js@1.8.11) 786 | '@solid-primitives/static-store': 0.0.5(solid-js@1.8.11) 787 | '@solid-primitives/utils': 6.2.2(solid-js@1.8.11) 788 | solid-js: 1.8.11 789 | dev: true 790 | 791 | /@solid-primitives/cursor@0.0.112(solid-js@1.8.11): 792 | resolution: {integrity: sha512-TAtU7qD7ipSLSXHnq8FhhosAPVX+dnOCb/ITcGcLlj8e/C9YKcxDhgBHJ3R/d1xDRb5/vO/szJtEz6fnQD311Q==} 793 | peerDependencies: 794 | solid-js: ^1.6.12 795 | dependencies: 796 | '@solid-primitives/utils': 6.2.2(solid-js@1.8.11) 797 | solid-js: 1.8.11 798 | dev: true 799 | 800 | /@solid-primitives/event-bus@1.0.9(solid-js@1.8.11): 801 | resolution: {integrity: sha512-BI9dla3GQzINsufEzr/CV3B/9e0D1pyk7Ig6kPI6s4geUkiVgFadY5V8R95PEeHH1NagOny7AHpw+RaRIuBpkA==} 802 | peerDependencies: 803 | solid-js: ^1.6.12 804 | dependencies: 805 | '@solid-primitives/utils': 6.2.2(solid-js@1.8.11) 806 | solid-js: 1.8.11 807 | dev: true 808 | 809 | /@solid-primitives/event-listener@2.3.1(solid-js@1.8.11): 810 | resolution: {integrity: sha512-S1AfFYatOJ3g/ZUbGDoKplSGLTTfarQ3Mfd3F/fXb9SnzGtROtd+Y6yLkPVzK4AVw83r2wUSaS0GS6dg8izTEQ==} 811 | peerDependencies: 812 | solid-js: ^1.6.12 813 | dependencies: 814 | '@solid-primitives/utils': 6.2.2(solid-js@1.8.11) 815 | solid-js: 1.8.11 816 | dev: true 817 | 818 | /@solid-primitives/keyboard@1.2.6(solid-js@1.8.11): 819 | resolution: {integrity: sha512-ZbXkzAwFs+6hyaZ2hT9uQ38iPZzGTjV6kWvVvJ3BDfKASg0dDDuhEZRKmpkmpLfnqOabV58BUkASJWMAA4dZCg==} 820 | peerDependencies: 821 | solid-js: ^1.6.12 822 | dependencies: 823 | '@solid-primitives/event-listener': 2.3.1(solid-js@1.8.11) 824 | '@solid-primitives/rootless': 1.4.3(solid-js@1.8.11) 825 | '@solid-primitives/utils': 6.2.2(solid-js@1.8.11) 826 | solid-js: 1.8.11 827 | dev: true 828 | 829 | /@solid-primitives/media@2.2.6(solid-js@1.8.11): 830 | resolution: {integrity: sha512-VopOSqoUZgmSFY4SNnwBzHYaoGG+7gQYcwX+RJ/qQtuZJgzOiC+PejZEwNJh+aBZ383HPwrypyd3zrYVm7EnpQ==} 831 | peerDependencies: 832 | solid-js: ^1.6.12 833 | dependencies: 834 | '@solid-primitives/event-listener': 2.3.1(solid-js@1.8.11) 835 | '@solid-primitives/rootless': 1.4.3(solid-js@1.8.11) 836 | '@solid-primitives/static-store': 0.0.6(solid-js@1.8.11) 837 | '@solid-primitives/utils': 6.2.2(solid-js@1.8.11) 838 | solid-js: 1.8.11 839 | dev: true 840 | 841 | /@solid-primitives/platform@0.1.1(solid-js@1.8.11): 842 | resolution: {integrity: sha512-Ln7dzHFjNDpjmhnKiMAUWBjObRZ01FQtj4ABkSmE51VjLI0i9JW54lbZhJImYjpCyoBjtxBEGWsCgYi9JyRXrA==} 843 | peerDependencies: 844 | solid-js: ^1.6.12 845 | dependencies: 846 | solid-js: 1.8.11 847 | dev: true 848 | 849 | /@solid-primitives/refs@1.0.6(solid-js@1.8.11): 850 | resolution: {integrity: sha512-ruh4YdVMxThEVnvqbpeLXKojW442vpFU8q7dSKtElGOTa31aKOAkRb9BTbdaTwVjN4BEq79fiiYIXozJNl4dSw==} 851 | peerDependencies: 852 | solid-js: ^1.6.12 853 | dependencies: 854 | '@solid-primitives/utils': 6.2.2(solid-js@1.8.11) 855 | solid-js: 1.8.11 856 | dev: true 857 | 858 | /@solid-primitives/resize-observer@2.0.23(solid-js@1.8.11): 859 | resolution: {integrity: sha512-SgKRzRfy1oFbPSapmtF5H9VQcN66foQaQK3QTyzh4cihIG2k/UiVNjbC+el1nEDnSeAP7zTO4Xsf2UW8dc+aBw==} 860 | peerDependencies: 861 | solid-js: ^1.6.12 862 | dependencies: 863 | '@solid-primitives/event-listener': 2.3.1(solid-js@1.8.11) 864 | '@solid-primitives/rootless': 1.4.3(solid-js@1.8.11) 865 | '@solid-primitives/static-store': 0.0.6(solid-js@1.8.11) 866 | '@solid-primitives/utils': 6.2.2(solid-js@1.8.11) 867 | solid-js: 1.8.11 868 | dev: true 869 | 870 | /@solid-primitives/rootless@1.4.3(solid-js@1.8.11): 871 | resolution: {integrity: sha512-IPsfUhKsqQOxLtRMQWK2EZAYbL9RKJMLBelLwpaXl9+oa1tl5aNvA6GHgrNrK+85oUhiYh7/OuogO18AuHepqQ==} 872 | peerDependencies: 873 | solid-js: ^1.6.12 874 | dependencies: 875 | '@solid-primitives/utils': 6.2.2(solid-js@1.8.11) 876 | solid-js: 1.8.11 877 | dev: true 878 | 879 | /@solid-primitives/scheduled@1.4.2(solid-js@1.8.11): 880 | resolution: {integrity: sha512-duKaugDQtPk0v6MnkBuEalWk66/vA2G7zzoimQEvmUdh2+K2o8t908HIfI2NdBfwakQMQBV4epE3TFeN2Vsveg==} 881 | peerDependencies: 882 | solid-js: ^1.6.12 883 | dependencies: 884 | solid-js: 1.8.11 885 | dev: true 886 | 887 | /@solid-primitives/static-store@0.0.5(solid-js@1.8.11): 888 | resolution: {integrity: sha512-ssQ+s/wrlFAEE4Zw8GV499yBfvWx7SMm+ZVc11wvao4T5xg9VfXCL9Oa+x4h+vPMvSV/Knv5LrsLiUa+wlJUXQ==} 889 | peerDependencies: 890 | solid-js: ^1.6.12 891 | dependencies: 892 | '@solid-primitives/utils': 6.2.2(solid-js@1.8.11) 893 | solid-js: 1.8.11 894 | dev: true 895 | 896 | /@solid-primitives/static-store@0.0.6(solid-js@1.8.11): 897 | resolution: {integrity: sha512-PtvkbbucbjT+9p95pksOciG9gOnCtJz4IUyAKX1Ld7YwI+QgtPTo0Wuxs8gNbNtLtoDv5PNv5t4YRzUyl0fwdg==} 898 | peerDependencies: 899 | solid-js: ^1.6.12 900 | dependencies: 901 | '@solid-primitives/utils': 6.2.2(solid-js@1.8.11) 902 | solid-js: 1.8.11 903 | dev: true 904 | 905 | /@solid-primitives/styles@0.0.111(solid-js@1.8.11): 906 | resolution: {integrity: sha512-1mBxOGAPXmfD5oYCvqjKBDN7SuNjz2qz7RdH7KtsuNLQh6lpuSKadtHnLvru0Y8Vz1InqTJisBIy/6P5kyDmPw==} 907 | peerDependencies: 908 | solid-js: ^1.6.12 909 | dependencies: 910 | '@solid-primitives/rootless': 1.4.3(solid-js@1.8.11) 911 | '@solid-primitives/utils': 6.2.2(solid-js@1.8.11) 912 | solid-js: 1.8.11 913 | dev: true 914 | 915 | /@solid-primitives/utils@6.2.2(solid-js@1.8.11): 916 | resolution: {integrity: sha512-11ypVbp987XxETeRqY5Y3OmmTpm8/jZqJXRvo6AyqBthzkvvjEdReuUMU2yVb+pwWGxfZpWHZ6EUCcGXUMhfwg==} 917 | peerDependencies: 918 | solid-js: ^1.6.12 919 | dependencies: 920 | solid-js: 1.8.11 921 | dev: true 922 | 923 | /@types/babel__core@7.20.5: 924 | resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} 925 | dependencies: 926 | '@babel/parser': 7.22.5 927 | '@babel/types': 7.22.5 928 | '@types/babel__generator': 7.6.4 929 | '@types/babel__template': 7.4.1 930 | '@types/babel__traverse': 7.20.1 931 | dev: true 932 | 933 | /@types/babel__generator@7.6.4: 934 | resolution: {integrity: sha512-tFkciB9j2K755yrTALxD44McOrk+gfpIpvC3sxHjRawj6PfnQxrse4Clq5y/Rq+G3mrBurMax/lG8Qn2t9mSsg==} 935 | dependencies: 936 | '@babel/types': 7.22.5 937 | dev: true 938 | 939 | /@types/babel__template@7.4.1: 940 | resolution: {integrity: sha512-azBFKemX6kMg5Io+/rdGT0dkGreboUVR0Cdm3fz9QJWpaQGJRQXl7C+6hOTCZcMll7KFyEQpgbYI2lHdsS4U7g==} 941 | dependencies: 942 | '@babel/parser': 7.22.5 943 | '@babel/types': 7.22.5 944 | dev: true 945 | 946 | /@types/babel__traverse@7.20.1: 947 | resolution: {integrity: sha512-MitHFXnhtgwsGZWtT68URpOvLN4EREih1u3QtQiN4VdAxWKRVvGCSvw/Qth0M0Qq3pJpnGOu5JaM/ydK7OGbqg==} 948 | dependencies: 949 | '@babel/types': 7.22.5 950 | dev: true 951 | 952 | /@types/estree@1.0.5: 953 | resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} 954 | dev: true 955 | 956 | /ansi-styles@3.2.1: 957 | resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} 958 | engines: {node: '>=4'} 959 | dependencies: 960 | color-convert: 1.9.3 961 | dev: true 962 | 963 | /babel-plugin-jsx-dom-expressions@0.37.13(@babel/core@7.23.7): 964 | resolution: {integrity: sha512-oAEMMIgU0h1DmHn4ZDaBBFc08nsVJciLq9pF7g0ZdpeIDKfY4zXjXr8+/oBjKhXG8nyomhnTodPjeG+/ZXcWXQ==} 965 | peerDependencies: 966 | '@babel/core': ^7.20.12 967 | dependencies: 968 | '@babel/core': 7.23.7 969 | '@babel/helper-module-imports': 7.18.6 970 | '@babel/plugin-syntax-jsx': 7.22.5(@babel/core@7.23.7) 971 | '@babel/types': 7.22.5 972 | html-entities: 2.3.3 973 | validate-html-nesting: 1.2.2 974 | dev: true 975 | 976 | /babel-preset-solid@1.8.9(@babel/core@7.23.7): 977 | resolution: {integrity: sha512-1awR1QCoryXtAdnjsrx/eVBTYz+tpHUDOdBXqG9oVV7S0ojf2MV/woR0+8BG+LMXVzIr60oKYzCZ9UZGafxmpg==} 978 | peerDependencies: 979 | '@babel/core': ^7.0.0 980 | dependencies: 981 | '@babel/core': 7.23.7 982 | babel-plugin-jsx-dom-expressions: 0.37.13(@babel/core@7.23.7) 983 | dev: true 984 | 985 | /browserslist@4.22.2: 986 | resolution: {integrity: sha512-0UgcrvQmBDvZHFGdYUehrCNIazki7/lUP3kkoi/r3YB2amZbFM9J43ZRkJTXBUZK4gmx56+Sqk9+Vs9mwZx9+A==} 987 | engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} 988 | hasBin: true 989 | dependencies: 990 | caniuse-lite: 1.0.30001579 991 | electron-to-chromium: 1.4.639 992 | node-releases: 2.0.14 993 | update-browserslist-db: 1.0.13(browserslist@4.22.2) 994 | dev: true 995 | 996 | /caniuse-lite@1.0.30001579: 997 | resolution: {integrity: sha512-u5AUVkixruKHJjw/pj9wISlcMpgFWzSrczLZbrqBSxukQixmg0SJ5sZTpvaFvxU0HoQKd4yoyAogyrAz9pzJnA==} 998 | dev: true 999 | 1000 | /chalk@2.4.2: 1001 | resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} 1002 | engines: {node: '>=4'} 1003 | dependencies: 1004 | ansi-styles: 3.2.1 1005 | escape-string-regexp: 1.0.5 1006 | supports-color: 5.5.0 1007 | dev: true 1008 | 1009 | /color-convert@1.9.3: 1010 | resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} 1011 | dependencies: 1012 | color-name: 1.1.3 1013 | dev: true 1014 | 1015 | /color-name@1.1.3: 1016 | resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} 1017 | dev: true 1018 | 1019 | /convert-source-map@2.0.0: 1020 | resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} 1021 | dev: true 1022 | 1023 | /csstype@3.1.2: 1024 | resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==} 1025 | 1026 | /debug@4.3.4: 1027 | resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} 1028 | engines: {node: '>=6.0'} 1029 | peerDependencies: 1030 | supports-color: '*' 1031 | peerDependenciesMeta: 1032 | supports-color: 1033 | optional: true 1034 | dependencies: 1035 | ms: 2.1.2 1036 | dev: true 1037 | 1038 | /electron-to-chromium@1.4.639: 1039 | resolution: {integrity: sha512-CkKf3ZUVZchr+zDpAlNLEEy2NJJ9T64ULWaDgy3THXXlPVPkLu3VOs9Bac44nebVtdwl2geSj6AxTtGDOxoXhg==} 1040 | dev: true 1041 | 1042 | /esbuild@0.19.11: 1043 | resolution: {integrity: sha512-HJ96Hev2hX/6i5cDVwcqiJBBtuo9+FeIJOtZ9W1kA5M6AMJRHUZlpYZ1/SbEwtO0ioNAW8rUooVpC/WehY2SfA==} 1044 | engines: {node: '>=12'} 1045 | hasBin: true 1046 | requiresBuild: true 1047 | optionalDependencies: 1048 | '@esbuild/aix-ppc64': 0.19.11 1049 | '@esbuild/android-arm': 0.19.11 1050 | '@esbuild/android-arm64': 0.19.11 1051 | '@esbuild/android-x64': 0.19.11 1052 | '@esbuild/darwin-arm64': 0.19.11 1053 | '@esbuild/darwin-x64': 0.19.11 1054 | '@esbuild/freebsd-arm64': 0.19.11 1055 | '@esbuild/freebsd-x64': 0.19.11 1056 | '@esbuild/linux-arm': 0.19.11 1057 | '@esbuild/linux-arm64': 0.19.11 1058 | '@esbuild/linux-ia32': 0.19.11 1059 | '@esbuild/linux-loong64': 0.19.11 1060 | '@esbuild/linux-mips64el': 0.19.11 1061 | '@esbuild/linux-ppc64': 0.19.11 1062 | '@esbuild/linux-riscv64': 0.19.11 1063 | '@esbuild/linux-s390x': 0.19.11 1064 | '@esbuild/linux-x64': 0.19.11 1065 | '@esbuild/netbsd-x64': 0.19.11 1066 | '@esbuild/openbsd-x64': 0.19.11 1067 | '@esbuild/sunos-x64': 0.19.11 1068 | '@esbuild/win32-arm64': 0.19.11 1069 | '@esbuild/win32-ia32': 0.19.11 1070 | '@esbuild/win32-x64': 0.19.11 1071 | dev: true 1072 | 1073 | /escalade@3.1.1: 1074 | resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} 1075 | engines: {node: '>=6'} 1076 | dev: true 1077 | 1078 | /escape-string-regexp@1.0.5: 1079 | resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} 1080 | engines: {node: '>=0.8.0'} 1081 | dev: true 1082 | 1083 | /fsevents@2.3.3: 1084 | resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} 1085 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 1086 | os: [darwin] 1087 | requiresBuild: true 1088 | dev: true 1089 | optional: true 1090 | 1091 | /gensync@1.0.0-beta.2: 1092 | resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} 1093 | engines: {node: '>=6.9.0'} 1094 | dev: true 1095 | 1096 | /globals@11.12.0: 1097 | resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} 1098 | engines: {node: '>=4'} 1099 | dev: true 1100 | 1101 | /has-flag@3.0.0: 1102 | resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} 1103 | engines: {node: '>=4'} 1104 | dev: true 1105 | 1106 | /html-entities@2.3.3: 1107 | resolution: {integrity: sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==} 1108 | dev: true 1109 | 1110 | /is-what@4.1.15: 1111 | resolution: {integrity: sha512-uKua1wfy3Yt+YqsD6mTUEa2zSi3G1oPlqTflgaPJ7z63vUGN5pxFpnQfeSLMFnJDEsdvOtkp1rUWkYjB4YfhgA==} 1112 | engines: {node: '>=12.13'} 1113 | dev: true 1114 | 1115 | /js-tokens@4.0.0: 1116 | resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} 1117 | dev: true 1118 | 1119 | /jsesc@2.5.2: 1120 | resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} 1121 | engines: {node: '>=4'} 1122 | hasBin: true 1123 | dev: true 1124 | 1125 | /json5@2.2.3: 1126 | resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} 1127 | engines: {node: '>=6'} 1128 | hasBin: true 1129 | dev: true 1130 | 1131 | /lru-cache@5.1.1: 1132 | resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} 1133 | dependencies: 1134 | yallist: 3.1.1 1135 | dev: true 1136 | 1137 | /merge-anything@5.1.7: 1138 | resolution: {integrity: sha512-eRtbOb1N5iyH0tkQDAoQ4Ipsp/5qSR79Dzrz8hEPxRX10RWWR/iQXdoKmBSRCThY1Fh5EhISDtpSc93fpxUniQ==} 1139 | engines: {node: '>=12.13'} 1140 | dependencies: 1141 | is-what: 4.1.15 1142 | dev: true 1143 | 1144 | /ms@2.1.2: 1145 | resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} 1146 | dev: true 1147 | 1148 | /nanoid@3.3.7: 1149 | resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} 1150 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 1151 | hasBin: true 1152 | dev: true 1153 | 1154 | /node-releases@2.0.14: 1155 | resolution: {integrity: sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw==} 1156 | dev: true 1157 | 1158 | /picocolors@1.0.0: 1159 | resolution: {integrity: sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==} 1160 | dev: true 1161 | 1162 | /postcss@8.4.33: 1163 | resolution: {integrity: sha512-Kkpbhhdjw2qQs2O2DGX+8m5OVqEcbB9HRBvuYM9pgrjEFUg30A9LmXNlTAUj4S9kgtGyrMbTzVjH7E+s5Re2yg==} 1164 | engines: {node: ^10 || ^12 || >=14} 1165 | dependencies: 1166 | nanoid: 3.3.7 1167 | picocolors: 1.0.0 1168 | source-map-js: 1.0.2 1169 | dev: true 1170 | 1171 | /rollup@4.9.5: 1172 | resolution: {integrity: sha512-E4vQW0H/mbNMw2yLSqJyjtkHY9dslf/p0zuT1xehNRqUTBOFMqEjguDvqhXr7N7r/4ttb2jr4T41d3dncmIgbQ==} 1173 | engines: {node: '>=18.0.0', npm: '>=8.0.0'} 1174 | hasBin: true 1175 | dependencies: 1176 | '@types/estree': 1.0.5 1177 | optionalDependencies: 1178 | '@rollup/rollup-android-arm-eabi': 4.9.5 1179 | '@rollup/rollup-android-arm64': 4.9.5 1180 | '@rollup/rollup-darwin-arm64': 4.9.5 1181 | '@rollup/rollup-darwin-x64': 4.9.5 1182 | '@rollup/rollup-linux-arm-gnueabihf': 4.9.5 1183 | '@rollup/rollup-linux-arm64-gnu': 4.9.5 1184 | '@rollup/rollup-linux-arm64-musl': 4.9.5 1185 | '@rollup/rollup-linux-riscv64-gnu': 4.9.5 1186 | '@rollup/rollup-linux-x64-gnu': 4.9.5 1187 | '@rollup/rollup-linux-x64-musl': 4.9.5 1188 | '@rollup/rollup-win32-arm64-msvc': 4.9.5 1189 | '@rollup/rollup-win32-ia32-msvc': 4.9.5 1190 | '@rollup/rollup-win32-x64-msvc': 4.9.5 1191 | fsevents: 2.3.3 1192 | dev: true 1193 | 1194 | /semver@6.3.1: 1195 | resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} 1196 | hasBin: true 1197 | dev: true 1198 | 1199 | /seroval-plugins@1.0.4(seroval@1.0.4): 1200 | resolution: {integrity: sha512-DQ2IK6oQVvy8k+c2V5x5YCtUa/GGGsUwUBNN9UqohrZ0rWdUapBFpNMYP1bCyRHoxOJjdKGl+dieacFIpU/i1A==} 1201 | engines: {node: '>=10'} 1202 | peerDependencies: 1203 | seroval: ^1.0 1204 | dependencies: 1205 | seroval: 1.0.4 1206 | 1207 | /seroval@1.0.4: 1208 | resolution: {integrity: sha512-qQs/N+KfJu83rmszFQaTxcoJoPn6KNUruX4KmnmyD0oZkUoiNvJ1rpdYKDf4YHM05k+HOgCxa3yvf15QbVijGg==} 1209 | engines: {node: '>=10'} 1210 | 1211 | /solid-devtools@0.29.2(solid-js@1.8.11)(vite@5.0.11): 1212 | resolution: {integrity: sha512-sfGLBSIQ3E5dFha5UCE5wKnz+War99JNAOggfBbwhltXL8lbIG1PnKKB9N2pvloZuFzMtT/u1Qbb/ubqXnw2Sg==} 1213 | peerDependencies: 1214 | solid-js: ^1.8.0 1215 | solid-start: ^0.3.0 1216 | vite: ^2.2.3 || ^3.0.0 || ^4.0.0 1217 | peerDependenciesMeta: 1218 | solid-start: 1219 | optional: true 1220 | vite: 1221 | optional: true 1222 | dependencies: 1223 | '@babel/core': 7.23.7 1224 | '@babel/plugin-syntax-typescript': 7.23.3(@babel/core@7.23.7) 1225 | '@babel/types': 7.23.6 1226 | '@solid-devtools/debugger': 0.23.3(solid-js@1.8.11) 1227 | '@solid-devtools/shared': 0.13.1(solid-js@1.8.11) 1228 | solid-js: 1.8.11 1229 | vite: 5.0.11 1230 | transitivePeerDependencies: 1231 | - supports-color 1232 | dev: true 1233 | 1234 | /solid-js@1.8.11: 1235 | resolution: {integrity: sha512-WdwmER+TwBJiN4rVQTVBxocg+9pKlOs41KzPYntrC86xO5sek8TzBYozPEZPL1IRWDouf2lMrvSbIs3CanlPvQ==} 1236 | dependencies: 1237 | csstype: 3.1.2 1238 | seroval: 1.0.4 1239 | seroval-plugins: 1.0.4(seroval@1.0.4) 1240 | 1241 | /solid-refresh@0.6.3(solid-js@1.8.11): 1242 | resolution: {integrity: sha512-F3aPsX6hVw9ttm5LYlth8Q15x6MlI/J3Dn+o3EQyRTtTxidepSTwAYdozt01/YA+7ObcciagGEyXIopGZzQtbA==} 1243 | peerDependencies: 1244 | solid-js: ^1.3 1245 | dependencies: 1246 | '@babel/generator': 7.23.6 1247 | '@babel/helper-module-imports': 7.22.15 1248 | '@babel/types': 7.23.6 1249 | solid-js: 1.8.11 1250 | dev: true 1251 | 1252 | /source-map-js@1.0.2: 1253 | resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} 1254 | engines: {node: '>=0.10.0'} 1255 | dev: true 1256 | 1257 | /supports-color@5.5.0: 1258 | resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} 1259 | engines: {node: '>=4'} 1260 | dependencies: 1261 | has-flag: 3.0.0 1262 | dev: true 1263 | 1264 | /to-fast-properties@2.0.0: 1265 | resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} 1266 | engines: {node: '>=4'} 1267 | dev: true 1268 | 1269 | /typescript@5.3.3: 1270 | resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==} 1271 | engines: {node: '>=14.17'} 1272 | hasBin: true 1273 | dev: true 1274 | 1275 | /update-browserslist-db@1.0.13(browserslist@4.22.2): 1276 | resolution: {integrity: sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg==} 1277 | hasBin: true 1278 | peerDependencies: 1279 | browserslist: '>= 4.21.0' 1280 | dependencies: 1281 | browserslist: 4.22.2 1282 | escalade: 3.1.1 1283 | picocolors: 1.0.0 1284 | dev: true 1285 | 1286 | /validate-html-nesting@1.2.2: 1287 | resolution: {integrity: sha512-hGdgQozCsQJMyfK5urgFcWEqsSSrK63Awe0t/IMR0bZ0QMtnuaiHzThW81guu3qx9abLi99NEuiaN6P9gVYsNg==} 1288 | dev: true 1289 | 1290 | /vite-plugin-solid@2.8.2(solid-js@1.8.11)(vite@5.0.11): 1291 | resolution: {integrity: sha512-HcvMs6DTxBaO4kE3psnirPQBCUUdYeQkCNKuB2TpEkJsxb6BGP6/7qkbbCSMxn25PyNdjvzVi1WXi0ou8KPgHw==} 1292 | peerDependencies: 1293 | solid-js: ^1.7.2 1294 | vite: ^3.0.0 || ^4.0.0 || ^5.0.0 1295 | dependencies: 1296 | '@babel/core': 7.23.7 1297 | '@babel/preset-typescript': 7.23.3(@babel/core@7.23.7) 1298 | '@types/babel__core': 7.20.5 1299 | babel-preset-solid: 1.8.9(@babel/core@7.23.7) 1300 | merge-anything: 5.1.7 1301 | solid-js: 1.8.11 1302 | solid-refresh: 0.6.3(solid-js@1.8.11) 1303 | vite: 5.0.11 1304 | vitefu: 0.2.5(vite@5.0.11) 1305 | transitivePeerDependencies: 1306 | - supports-color 1307 | dev: true 1308 | 1309 | /vite@5.0.11: 1310 | resolution: {integrity: sha512-XBMnDjZcNAw/G1gEiskiM1v6yzM4GE5aMGvhWTlHAYYhxb7S3/V1s3m2LDHa8Vh6yIWYYB0iJwsEaS523c4oYA==} 1311 | engines: {node: ^18.0.0 || >=20.0.0} 1312 | hasBin: true 1313 | peerDependencies: 1314 | '@types/node': ^18.0.0 || >=20.0.0 1315 | less: '*' 1316 | lightningcss: ^1.21.0 1317 | sass: '*' 1318 | stylus: '*' 1319 | sugarss: '*' 1320 | terser: ^5.4.0 1321 | peerDependenciesMeta: 1322 | '@types/node': 1323 | optional: true 1324 | less: 1325 | optional: true 1326 | lightningcss: 1327 | optional: true 1328 | sass: 1329 | optional: true 1330 | stylus: 1331 | optional: true 1332 | sugarss: 1333 | optional: true 1334 | terser: 1335 | optional: true 1336 | dependencies: 1337 | esbuild: 0.19.11 1338 | postcss: 8.4.33 1339 | rollup: 4.9.5 1340 | optionalDependencies: 1341 | fsevents: 2.3.3 1342 | dev: true 1343 | 1344 | /vitefu@0.2.5(vite@5.0.11): 1345 | resolution: {integrity: sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==} 1346 | peerDependencies: 1347 | vite: ^3.0.0 || ^4.0.0 || ^5.0.0 1348 | peerDependenciesMeta: 1349 | vite: 1350 | optional: true 1351 | dependencies: 1352 | vite: 5.0.11 1353 | dev: true 1354 | 1355 | /yallist@3.1.1: 1356 | resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} 1357 | dev: true 1358 | -------------------------------------------------------------------------------- /examples/solidjs-with-layout/public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feature-sliced/examples/e0da001376d82bb71aab2bd2c60c1bab9dd7a01c/examples/solidjs-with-layout/public/favicon.png -------------------------------------------------------------------------------- /examples/solidjs-with-layout/src/app/App.tsx: -------------------------------------------------------------------------------- 1 | import { Route, Router } from '@solidjs/router' 2 | import { BaseLayout } from './layouts' 3 | import { HomePage } from '@/pages/home' 4 | import { AboutPage } from '@/pages/about' 5 | import { NotFound } from '@/pages/404' 6 | 7 | export default function App() { 8 | return ( 9 | 10 | 11 | 12 | 13 | 14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /examples/solidjs-with-layout/src/app/entry.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | 15 | html, 16 | body, 17 | #root { 18 | min-height: 100%; 19 | min-height: 100vh; 20 | padding: 0; 21 | margin: 0; 22 | } 23 | 24 | [data-fsd^='widget'] { 25 | --fsd-color: #fa0ee9; 26 | } 27 | 28 | [data-fsd^='feature'] { 29 | --fsd-color: #14a200; 30 | } 31 | 32 | [data-fsd^='entity'] { 33 | --fsd-color: #2573e5; 34 | } 35 | 36 | [data-fsd^='shared'] { 37 | --fsd-color: #b6bc59; 38 | } 39 | 40 | [data-fsd] { 41 | outline: 2px solid var(--fsd-color); 42 | position: relative; 43 | } 44 | 45 | [data-fsd]::after { 46 | content: attr(data-fsd); 47 | pointer-events: none; 48 | position: absolute; 49 | top: 0; 50 | right: 0; 51 | box-sizing: border-box; 52 | background: var(--fsd-color); 53 | color: #fff; 54 | font-size: 10px; 55 | line-height: 1; 56 | font-weight: bold; 57 | padding: 0 2px; 58 | font-family: sans-serif; 59 | } 60 | 61 | [data-fsd]::before { 62 | pointer-events: none; 63 | position: absolute; 64 | top: 0; 65 | right: 0; 66 | left: 0; 67 | bottom: 0; 68 | background: color-mix(in srgb, var(--fsd-color) 15%, transparent); 69 | content: ''; 70 | z-index: 1; 71 | opacity: 0.8; 72 | } 73 | -------------------------------------------------------------------------------- /examples/solidjs-with-layout/src/app/entry.tsx: -------------------------------------------------------------------------------- 1 | /* @refresh reload */ 2 | import { render } from 'solid-js/web' 3 | 4 | import './entry.css' 5 | import App from './App' 6 | 7 | const root = document.getElementById('root') 8 | 9 | if (import.meta.env.DEV && !(root instanceof HTMLElement)) { 10 | throw new Error( 11 | 'Root element not found. Did you forget to add it to your index.html? Or maybe the id attribute got misspelled?', 12 | ) 13 | } 14 | 15 | render(() => , root!) 16 | -------------------------------------------------------------------------------- /examples/solidjs-with-layout/src/app/layouts/BaseLayout.tsx: -------------------------------------------------------------------------------- 1 | import type { RouteSectionProps } from '@solidjs/router' 2 | import { Layout } from '@/shared/ui' 3 | import { Header } from '@/widgets/header' 4 | 5 | type Props = RouteSectionProps 6 | 7 | /** 8 | * ✅ FSD Best practice: 9 | * 10 | * (1) Divide layout in two modules: dumb layout grid (`@shared/ui/Layout/*`) 11 | * and smart layout with widgets (this file) 12 | * 13 | * (2) Avoid importing from higher layers with dependency inversion 14 | * via the render prop pattern 15 | */ 16 | export function BaseLayout(props: Props) { 17 | return ( 18 | }> 19 | {props.children} 20 | 21 | ) 22 | } 23 | -------------------------------------------------------------------------------- /examples/solidjs-with-layout/src/app/layouts/index.ts: -------------------------------------------------------------------------------- 1 | export { BaseLayout } from './BaseLayout' 2 | -------------------------------------------------------------------------------- /examples/solidjs-with-layout/src/pages/404.tsx: -------------------------------------------------------------------------------- 1 | import { Title } from '@solidjs/meta' 2 | import { A } from '@solidjs/router' 3 | 4 | export function NotFound() { 5 | return ( 6 |
7 | Not Found 8 |

Page Not Found

9 |

10 | Page not found. 11 | Return to main page 12 |

13 |
14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /examples/solidjs-with-layout/src/pages/about.tsx: -------------------------------------------------------------------------------- 1 | import { Title } from '@solidjs/meta' 2 | 3 | export function AboutPage() { 4 | return ( 5 |
6 | About 7 |

About

8 |

9 | Visit 10 | {' '} 11 | feature-sliced/examples 12 | {' '} 13 | to see more examples. 14 |

15 |
16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /examples/solidjs-with-layout/src/pages/home.tsx: -------------------------------------------------------------------------------- 1 | import { Title } from '@solidjs/meta' 2 | 3 | export function HomePage() { 4 | return ( 5 |
6 | Home 7 |

examples/solidjs-with-layout

8 |

9 | This example shows how to work work with layout (or layouts). Split layout to dumb component (with markup) and smart component for widget compositions. 10 |

11 |

12 | Dumb layout can be placed in the shared layer, smart - in the app layer. 13 |

14 |
15 | ) 16 | } 17 | -------------------------------------------------------------------------------- /examples/solidjs-with-layout/src/shared/ui/Layout/Layout.module.css: -------------------------------------------------------------------------------- 1 | .root { 2 | display: flex; 3 | flex-direction: column; 4 | max-width: 1024px; 5 | width: calc(100% - 8px); 6 | margin: 4px auto; 7 | min-height: calc(100vh - 8px); 8 | gap: 12px; 9 | } 10 | 11 | .header { 12 | padding: 24px; 13 | } 14 | 15 | .footer { 16 | padding: 24px; 17 | } 18 | 19 | .main { 20 | padding: 24px; 21 | flex-grow: 1; 22 | } 23 | -------------------------------------------------------------------------------- /examples/solidjs-with-layout/src/shared/ui/Layout/Layout.tsx: -------------------------------------------------------------------------------- 1 | import { MetaProvider, Title } from '@solidjs/meta' 2 | import type { JSXElement } from 'solid-js' 3 | import { Suspense } from 'solid-js' 4 | import css from './Layout.module.css' 5 | 6 | type Props = { 7 | headerSlot: JSXElement 8 | children: JSXElement 9 | } 10 | 11 | export function Layout(props: Props) { 12 | return ( 13 | 14 |
15 | solidjs-with-layout 16 |
17 | {props.headerSlot} 18 |
19 |
20 | {props.children} 21 |
22 |
Footer
23 |
24 |
25 | ) 26 | } 27 | -------------------------------------------------------------------------------- /examples/solidjs-with-layout/src/shared/ui/index.ts: -------------------------------------------------------------------------------- 1 | export { Layout } from './Layout/Layout' 2 | -------------------------------------------------------------------------------- /examples/solidjs-with-layout/src/widgets/header/index.ts: -------------------------------------------------------------------------------- 1 | export { Header } from './ui/Header' 2 | -------------------------------------------------------------------------------- /examples/solidjs-with-layout/src/widgets/header/ui/Header.module.css: -------------------------------------------------------------------------------- 1 | .root { 2 | padding: 25px 0; 3 | display: flex; 4 | gap: 12px; 5 | } 6 | -------------------------------------------------------------------------------- /examples/solidjs-with-layout/src/widgets/header/ui/Header.tsx: -------------------------------------------------------------------------------- 1 | import css from './Header.module.css' 2 | 3 | export function Header() { 4 | return ( 5 |
6 | Home 7 | About 8 |
9 | ) 10 | } 11 | -------------------------------------------------------------------------------- /examples/solidjs-with-layout/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "jsx": "preserve", 5 | "jsxImportSource": "solid-js", 6 | "baseUrl": "./src", 7 | "module": "ESNext", 8 | "moduleResolution": "node", 9 | "paths": { 10 | "@/*": ["*"] 11 | }, 12 | "types": ["vite/client"], 13 | "strict": true, 14 | "noEmit": true, 15 | "allowSyntheticDefaultImports": true, 16 | "esModuleInterop": true, 17 | "isolatedModules": true 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /examples/solidjs-with-layout/vite.config.mts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import solidPlugin from 'vite-plugin-solid' 3 | import tsconfigPaths from 'vite-tsconfig-paths' 4 | // import devtools from 'solid-devtools/vite'; 5 | 6 | export default defineConfig({ 7 | plugins: [ 8 | /* 9 | Uncomment the following line to enable solid-devtools. 10 | For more info see https://github.com/thetarnav/solid-devtools/tree/main/packages/extension#readme 11 | */ 12 | // devtools(), 13 | solidPlugin(), 14 | tsconfigPaths(), 15 | ], 16 | server: { 17 | port: 3000, 18 | }, 19 | build: { 20 | target: 'esnext', 21 | }, 22 | }) 23 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "name": "feature-sliced-design-examples" 4 | } 5 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - 'examples/**' 3 | -------------------------------------------------------------------------------- /todo-app/.env: -------------------------------------------------------------------------------- 1 | REACT_APP_API_URL=https://jsonplaceholder.typicode.com 2 | -------------------------------------------------------------------------------- /todo-app/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | /.vscode 26 | /.idea 27 | -------------------------------------------------------------------------------- /todo-app/README.md: -------------------------------------------------------------------------------- 1 | # TodoApp (QuickStart) 2 | 3 | 4 | 5 | ## Available Scripts 6 | 7 | In the project directory, you can run: 8 | 9 | ### `npm start` 10 | 11 | Runs the app in the development mode.\ 12 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 13 | 14 | The page will reload if you make edits.\ 15 | You will also see any lint errors in the console. 16 | 17 | ### `npm test` 18 | 19 | Launches the test runner in the interactive watch mode.\ 20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 21 | 22 | ### `npm run build` 23 | 24 | Builds the app for production to the `build` folder.\ 25 | It correctly bundles React in production mode and optimizes the build for the best performance. 26 | 27 | The build is minified and the filenames include the hashes.\ 28 | Your app is ready to be deployed! 29 | 30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 31 | 32 | ### `npm run eject` 33 | 34 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!** 35 | 36 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 37 | 38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. 39 | 40 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 41 | 42 | ## Learn More 43 | 44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 45 | 46 | To learn React, check out the [React documentation](https://reactjs.org/). 47 | -------------------------------------------------------------------------------- /todo-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "todo-app", 3 | "version": "0.1.2", 4 | "private": true, 5 | "dependencies": { 6 | "@ant-design/icons": "^4.6.2", 7 | "@effector/reflect": "^0.6.0", 8 | "@testing-library/jest-dom": "^5.13.0", 9 | "@testing-library/react": "^11.2.7", 10 | "@testing-library/user-event": "^12.8.3", 11 | "@types/jest": "^26.0.23", 12 | "@types/node": "^12.20.14", 13 | "@types/react": "^17.0.9", 14 | "@types/react-dom": "^17.0.6", 15 | "antd": "^4.16.2", 16 | "axios": "^0.21.1", 17 | "classnames": "^2.3.1", 18 | "compose-function": "^3.0.3", 19 | "effector": "^21.8.11", 20 | "effector-react": "^21.3.2", 21 | "immer": "^8.0.1", 22 | "normalizr": "^3.6.1", 23 | "react": "^17.0.2", 24 | "react-dom": "^17.0.2", 25 | "react-router": "^5.2.0", 26 | "react-router-dom": "^5.2.0", 27 | "react-scripts": "4.0.3", 28 | "sass": "1.34.1", 29 | "typescript": "^4.3.2", 30 | "web-vitals": "^1.1.2" 31 | }, 32 | "scripts": { 33 | "start": "react-scripts start", 34 | "build": "react-scripts build", 35 | "test": "react-scripts test", 36 | "eject": "react-scripts eject" 37 | }, 38 | "eslintConfig": { 39 | "extends": [ 40 | "react-app", 41 | "react-app/jest" 42 | ] 43 | }, 44 | "browserslist": { 45 | "production": [ 46 | ">0.2%", 47 | "not dead", 48 | "not op_mini all" 49 | ], 50 | "development": [ 51 | "last 1 chrome version", 52 | "last 1 firefox version", 53 | "last 1 safari version" 54 | ] 55 | }, 56 | "devDependencies": { 57 | "@types/compose-function": "^0.0.30", 58 | "@types/react-router": "^5.1.14", 59 | "@types/react-router-dom": "^5.1.7" 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /todo-app/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feature-sliced/examples/e0da001376d82bb71aab2bd2c60c1bab9dd7a01c/todo-app/public/favicon.ico -------------------------------------------------------------------------------- /todo-app/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | Todo App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /todo-app/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feature-sliced/examples/e0da001376d82bb71aab2bd2c60c1bab9dd7a01c/todo-app/public/logo192.png -------------------------------------------------------------------------------- /todo-app/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/feature-sliced/examples/e0da001376d82bb71aab2bd2c60c1bab9dd7a01c/todo-app/public/logo512.png -------------------------------------------------------------------------------- /todo-app/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "Todo App", 3 | "name": "TodoApp Example (by feature-sliced)", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /todo-app/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /todo-app/src/app/index.scss: -------------------------------------------------------------------------------- 1 | @import "./styles/index.scss"; 2 | -------------------------------------------------------------------------------- /todo-app/src/app/index.tsx: -------------------------------------------------------------------------------- 1 | import { Routing } from "pages"; 2 | import { withProviders } from "./providers"; 3 | import './index.scss'; 4 | 5 | const App = () => { 6 | return ( 7 |
8 | 9 |
10 | ); 11 | } 12 | 13 | export default withProviders(App); 14 | -------------------------------------------------------------------------------- /todo-app/src/app/providers/index.ts: -------------------------------------------------------------------------------- 1 | import compose from "compose-function"; 2 | import { withRouter } from "./with-router"; 3 | 4 | export const withProviders = compose(withRouter); 5 | -------------------------------------------------------------------------------- /todo-app/src/app/providers/with-router.tsx: -------------------------------------------------------------------------------- 1 | import { Suspense } from "react"; 2 | import { BrowserRouter } from "react-router-dom"; 3 | import { Spin } from "antd"; // ~ shared/ui/sping 4 | 5 | export const withRouter = (component: () => React.ReactNode) => () => ( 6 | 7 | }> 8 | {component()} 9 | 10 | 11 | ); 12 | -------------------------------------------------------------------------------- /todo-app/src/app/styles/index.scss: -------------------------------------------------------------------------------- 1 | @import 'antd/dist/antd.css'; 2 | @import "./normalize.scss"; 3 | @import "./normalize-antd.scss"; 4 | @import "./vars.scss"; 5 | -------------------------------------------------------------------------------- /todo-app/src/app/styles/normalize-antd.scss: -------------------------------------------------------------------------------- 1 | // Лоадер на весь контейнер 2 | .ant-spin.overlay { 3 | position: absolute; 4 | z-index: 9999; 5 | display: flex; 6 | align-items: center; 7 | justify-content: center; 8 | width: 100%; 9 | height: 100%; 10 | background: rgba(255, 255, 255, 0.5); 11 | } 12 | -------------------------------------------------------------------------------- /todo-app/src/app/styles/normalize.scss: -------------------------------------------------------------------------------- 1 | html, 2 | body, 3 | #root, 4 | .app { 5 | position: relative; 6 | height: 100%; 7 | } 8 | 9 | html { 10 | scroll-behavior: smooth; 11 | } 12 | 13 | body { 14 | margin: 0; 15 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 16 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 17 | sans-serif; 18 | -webkit-font-smoothing: antialiased; 19 | -moz-osx-font-smoothing: grayscale; 20 | } 21 | 22 | code { 23 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 24 | monospace; 25 | } 26 | 27 | 28 | // scrollbar 29 | ::-webkit-scrollbar { 30 | width: 8px; 31 | height: 8px; 32 | background-color: transparent; 33 | 34 | &-track { 35 | background-color: transparent; 36 | border-radius: 5px; 37 | } 38 | 39 | &-thumb { 40 | background-color: #a6b2c3; 41 | border-radius: 5px; 42 | } 43 | 44 | &-corner { 45 | background-color: #ffffff; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /todo-app/src/app/styles/vars.scss: -------------------------------------------------------------------------------- 1 | :root { 2 | --color-dark: #242424; 3 | --color-primary: #108ee9; 4 | } 5 | -------------------------------------------------------------------------------- /todo-app/src/entities/task/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./ui"; 2 | export * as taskModel from "./model"; 3 | export * as taskLib from "./lib"; 4 | -------------------------------------------------------------------------------- /todo-app/src/entities/task/lib.ts: -------------------------------------------------------------------------------- 1 | import type { Task } from "shared/api"; 2 | 3 | export const getTaskStatus = (data: Task) => { 4 | return data.completed ? "CLOSED" : "OPENED"; 5 | }; 6 | -------------------------------------------------------------------------------- /todo-app/src/entities/task/model/index.ts: -------------------------------------------------------------------------------- 1 | // Разбиваем на submodels 2 | 3 | export * from "./tasks"; 4 | -------------------------------------------------------------------------------- /todo-app/src/entities/task/model/tasks.ts: -------------------------------------------------------------------------------- 1 | import { createStore, combine, createEffect, createEvent } from "effector"; 2 | import { useStore } from "effector-react"; 3 | import { normalize, schema } from "normalizr"; 4 | 5 | import { typicodeApi } from "shared/api"; 6 | import type { Task } from "shared/api"; 7 | 8 | export type QueryConfig = { 9 | completed?: boolean; 10 | userId?: number; 11 | }; 12 | 13 | 14 | const setQueryConfig = createEvent(); 15 | 16 | 17 | // В каждом эффекте так же может быть своя доп. обработка 18 | const getTasksListFx = createEffect((params?: typicodeApi.tasks.GetTasksListParams) => { 19 | return typicodeApi.tasks.getTasksList(params); 20 | }); 21 | const getTaskByIdFx = createEffect((params: typicodeApi.tasks.GetTaskByIdParams) => { 22 | return typicodeApi.tasks.getTaskById(params); 23 | }); 24 | 25 | 26 | // Можно вынести нормализацию на уровне API 27 | export const taskSchema = new schema.Entity("tasks"); 28 | export const normalizeTask = (data: Task) => normalize(data, taskSchema); 29 | export const normalizeTasks = (data: Task[]) => normalize(data, [taskSchema]); 30 | 31 | 32 | // В рамках демо некритично, но можно хранить и в виде массива без нормализации 33 | export const tasksInitialState: Record = {}; 34 | export const $tasks = createStore(tasksInitialState) 35 | .on(getTasksListFx.doneData, (_, payload) => normalizeTasks(payload.data).entities.tasks) 36 | .on(getTaskByIdFx.doneData, (state, payload) => ({ 37 | ...state, 38 | ...normalizeTask(payload.data).entities.tasks, 39 | })) 40 | 41 | 42 | // Можно вынести в отдельную директорию (для хранения нескольких моделей) 43 | export const $queryConfig = createStore({}) 44 | .on(setQueryConfig, (_, payload) => payload) 45 | 46 | // Можно добавить потенциально debounce логику 47 | export const $tasksListLoading = getTasksListFx.pending; 48 | export const $taskDetailsLoading = getTaskByIdFx.pending; 49 | 50 | 51 | /** 52 | * "Список" задач 53 | */ 54 | export const $tasksList = combine($tasks, (tasks) => Object.values(tasks)); 55 | 56 | /** 57 | * Отфильтрованные таски 58 | * @remark Можно разруливать на уровне эффектов - но тогда нужно подключать дополнительную логику в стор 59 | * > Например скрывать/показывать таск при `toggleTask` событии 60 | */ 61 | export const $tasksFiltered = combine( 62 | $tasksList, 63 | $queryConfig, 64 | (tasksList, config) => { 65 | return tasksList.filter(task => ( 66 | config.completed === undefined || 67 | task.completed === config.completed 68 | ))}, 69 | ); 70 | 71 | export const $tasksListEmpty = $tasksFiltered.map((list) => list.length === 0); 72 | 73 | // При желании можно завести отдельный селектор, не завязанный на react биндинги 74 | const useTask = (taskId: number): import("shared/api").Task | undefined => { 75 | return useStore($tasks)[taskId]; 76 | }; 77 | 78 | export const events = { setQueryConfig }; 79 | 80 | export const effects = { 81 | getTaskByIdFx, 82 | getTasksListFx, 83 | }; 84 | 85 | export const selectors = { 86 | useTask, 87 | }; 88 | -------------------------------------------------------------------------------- /todo-app/src/entities/task/ui/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./task-card"; 2 | export * from "./task-row"; 3 | -------------------------------------------------------------------------------- /todo-app/src/entities/task/ui/task-card/index.tsx: -------------------------------------------------------------------------------- 1 | import type { PropsWithChildren } from "react"; 2 | import { Card } from "antd"; // ~ "shared/ui/card" 3 | import { Link } from "react-router-dom"; 4 | import styles from "./styles.module.scss"; 5 | 6 | export type TaskCardProps = PropsWithChildren<{ 7 | data?: import("shared/api").Task; 8 | titleHref?: string; 9 | }> & import("antd").CardProps; 10 | 11 | export const TaskCard = ({ data, titleHref, children, ...cardProps }: TaskCardProps) => { 12 | // Можно обработать и получше при желании 13 | if (!data && !cardProps.loading) return null; 14 | 15 | return ( 16 | 22 | {titleHref ? {data?.title} : data?.title} 23 | {children} 24 | 25 | ); 26 | }; 27 | -------------------------------------------------------------------------------- /todo-app/src/entities/task/ui/task-card/styles.module.scss: -------------------------------------------------------------------------------- 1 | .root { 2 | padding: 40px; 3 | 4 | & + & { 5 | margin: 20px; 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /todo-app/src/entities/task/ui/task-row/index.tsx: -------------------------------------------------------------------------------- 1 | import type { PropsWithChildren, ReactNode } from "react"; 2 | import cn from "classnames"; // Можно смело использовать аналоги 3 | import { Row } from "antd"; // ~ "shared/ui/row" 4 | import { Link } from "react-router-dom"; 5 | import styles from "./styles.module.scss"; 6 | 7 | export type TaskRowProps = PropsWithChildren<{ 8 | data: import("shared/api").Task; 9 | titleHref?: string; 10 | before?: ReactNode; 11 | }>; 12 | 13 | export const TaskRow = ({ data, before, titleHref }: TaskRowProps) => { 14 | const title = titleHref ? {data.title} : data.title 15 | 16 | return ( 17 | // Можно смело использовать classnames и аналоги 18 | 19 | {before} 20 | {title} 21 | 22 | ) 23 | } -------------------------------------------------------------------------------- /todo-app/src/entities/task/ui/task-row/styles.module.scss: -------------------------------------------------------------------------------- 1 | .root { 2 | background: #fff; 3 | padding: 20px; 4 | transition: 0.25s; 5 | } 6 | 7 | .completed { 8 | // Можно и получше подобрать стили 9 | text-decoration: line-through; 10 | opacity: 0.5; 11 | }; 12 | -------------------------------------------------------------------------------- /todo-app/src/features/tasks-filters/config.ts: -------------------------------------------------------------------------------- 1 | export type Filter = { 2 | id: number; 3 | title: string; 4 | config: import("entities/task").taskModel.QueryConfig; 5 | } 6 | 7 | // Описываем здесь датасет фильтров "Закрытые" / "Открытые" и т.п. 8 | export const filters: Record = { 9 | 1: { 10 | id: 1, 11 | title: "All", 12 | config: {}, 13 | }, 14 | 2: { 15 | id: 2, 16 | title: "Opened", 17 | config: { completed: false }, 18 | }, 19 | 3: { 20 | id: 3, 21 | title: "Closed", 22 | config: { completed: true }, 23 | }, 24 | }; 25 | 26 | export const DEFAULT_FILTER = 1; 27 | 28 | export const filtersList = Object.values(filters); 29 | 30 | export const getFilterById = (id: number) => filters[id]; 31 | -------------------------------------------------------------------------------- /todo-app/src/features/tasks-filters/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./ui"; 2 | -------------------------------------------------------------------------------- /todo-app/src/features/tasks-filters/ui.tsx: -------------------------------------------------------------------------------- 1 | import { Radio } from "antd"; // ~ "shared/ui/radio" 2 | import { reflect } from "@effector/reflect"; 3 | 4 | import { taskModel } from "entities/task"; 5 | import { filtersList, getFilterById, DEFAULT_FILTER } from "./config"; 6 | 7 | type Props = { 8 | loading: boolean; 9 | onFilterClick: (p: taskModel.QueryConfig) => void; 10 | }; 11 | 12 | const View = ({ loading, onFilterClick }: Props) => { 13 | return ( 14 | 15 | {filtersList.map(({ title, id }) => ( 16 | onFilterClick(getFilterById(id).config)} 19 | value={id} 20 | disabled={loading} 21 | > 22 | {title} 23 | 24 | ))} 25 | 26 | ); 27 | }; 28 | 29 | // Использование effector-reflect здесь опционально и некритично в рамках методологии 30 | export const TasksFilters = reflect({ 31 | view: View, 32 | bind: { 33 | loading: taskModel.$tasksListLoading, 34 | onFilterClick: taskModel.events.setQueryConfig, 35 | }, 36 | }); 37 | -------------------------------------------------------------------------------- /todo-app/src/features/toggle-task/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./ui"; 2 | export * as toggleTaskModel from "./model"; 3 | -------------------------------------------------------------------------------- /todo-app/src/features/toggle-task/model/index.ts: -------------------------------------------------------------------------------- 1 | export * from "./toggle-task"; 2 | -------------------------------------------------------------------------------- /todo-app/src/features/toggle-task/model/toggle-task.ts: -------------------------------------------------------------------------------- 1 | import { createEvent } from "effector"; 2 | import produce from "immer"; 3 | 4 | import { taskModel } from "entities/task"; 5 | 6 | const toggleTask = createEvent(); 7 | 8 | taskModel.$tasks.on(toggleTask, (state, taskId) => 9 | produce(state, (draft) => { 10 | const task = draft[taskId]; 11 | task.completed = !task.completed; 12 | }) 13 | ); 14 | 15 | export const events = { toggleTask }; 16 | -------------------------------------------------------------------------------- /todo-app/src/features/toggle-task/ui.tsx: -------------------------------------------------------------------------------- 1 | import { Checkbox } from "antd"; // ~ "shared/ui/checkbox" 2 | 3 | import { taskModel, taskLib } from "entities/task"; 4 | import * as toggleTaskModel from './model'; 5 | 6 | export type ToggleTaskProps = { 7 | taskId: number; 8 | withStatus?: boolean; 9 | } 10 | 11 | // resolve / unresolve 12 | export const ToggleTask = ({ taskId, withStatus = true }: ToggleTaskProps) => { 13 | const task = taskModel.selectors.useTask(taskId); 14 | 15 | if (!task) return null; 16 | 17 | const status = taskLib.getTaskStatus(task); 18 | 19 | return ( 20 | toggleTaskModel.events.toggleTask(taskId)} 22 | checked={task.completed} 23 | > 24 | {withStatus && status} 25 | 26 | ) 27 | } 28 | -------------------------------------------------------------------------------- /todo-app/src/index.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from 'app'; 4 | import reportWebVitals from './reportWebVitals'; 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root') 11 | ); 12 | 13 | // If you want to start measuring performance in your app, pass a function 14 | // to log results (for example: reportWebVitals(console.log)) 15 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 16 | reportWebVitals(); 17 | -------------------------------------------------------------------------------- /todo-app/src/pages/index.tsx: -------------------------------------------------------------------------------- 1 | // Либо использовать @loadable/component, в рамках туториала - некритично 2 | import { lazy } from "react"; 3 | import { Route, Switch, Redirect } from "react-router-dom"; 4 | 5 | const TasksListPage = lazy(() => import("./tasks-list")); 6 | const TaskDetailsPage = lazy(() => import("./task-details")); 7 | 8 | export const Routing = () => { 9 | return ( 10 | 11 | 12 | 13 | 14 | 15 | ); 16 | }; 17 | -------------------------------------------------------------------------------- /todo-app/src/pages/task-details/index.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { Link } from "react-router-dom"; 3 | import { Layout, Result, Button } from "antd"; // ~ "shared/ui/{...}" 4 | import { reflect } from "@effector/reflect"; 5 | 6 | import { ToggleTask } from "features/toggle-task"; 7 | import { TaskCard, taskModel } from "entities/task"; 8 | import styles from "./styles.module.scss"; 9 | 10 | type Props = import("react-router-dom").RouteChildrenProps<{ 11 | taskId: string; 12 | }> & { 13 | isLoading: boolean; 14 | }; 15 | 16 | const View = ({ match, isLoading }: Props) => { 17 | const taskId = Number(match?.params.taskId); 18 | const task = taskModel.selectors.useTask(taskId); 19 | 20 | useEffect(() => { 21 | taskModel.effects.getTaskByIdFx({ taskId }); 22 | }, [taskId]); 23 | 24 | // Можно часть логики перенести в entity/task/card (как контейнер) 25 | if (!task && !isLoading) { 26 | return ( 27 | } 32 | /> 33 | ) 34 | } 35 | 36 | return ( 37 | 38 | 39 | Back to TasksList} 46 | actions={[ 47 | 48 | ]} 49 | /> 50 | 51 | 52 | ) 53 | }; 54 | 55 | // Использование effector-reflect здесь опционально и некритично в рамках методологии 56 | const TaskDetailsPage = reflect({ 57 | view: View, 58 | bind: { 59 | isLoading: taskModel.$taskDetailsLoading, 60 | } 61 | }); 62 | 63 | export default TaskDetailsPage; 64 | -------------------------------------------------------------------------------- /todo-app/src/pages/task-details/styles.module.scss: -------------------------------------------------------------------------------- 1 | .root { 2 | min-height: 100%; 3 | } 4 | 5 | .content { 6 | display: flex; 7 | align-items: center; 8 | justify-content: center; 9 | } 10 | 11 | .card { 12 | width: 60%; 13 | } -------------------------------------------------------------------------------- /todo-app/src/pages/tasks-list/index.tsx: -------------------------------------------------------------------------------- 1 | import { Layout, Row, Col, Typography, Spin, Empty } from "antd"; // ~ "shared/ui/{...}" 2 | import { variant, list } from "@effector/reflect"; 3 | import { combine } from "effector"; 4 | 5 | import { TasksFilters } from "features/tasks-filters"; 6 | import { ToggleTask } from "features/toggle-task"; 7 | import { TaskRow, taskModel } from "entities/task"; 8 | import styles from "./styles.module.scss"; 9 | 10 | const TasksListPage = () => { 11 | return ( 12 | 13 | 14 | {/* ~ Layout.Toolbar */} 15 | 16 | Tasks List 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | ); 29 | }; 30 | 31 | const ListItemView: React.FC<{ task: import("shared/api").Task }> = ({ task }) => { 32 | return ( 33 | 34 | } 38 | /> 39 | 40 | ); 41 | }; 42 | 43 | // Использование effector-reflect здесь опционально и некритично в рамках методологии 44 | const TasksList = list({ 45 | view: ListItemView, 46 | source: taskModel.$tasksFiltered, 47 | bind: {}, 48 | mapItem: { 49 | task: (task) => task, 50 | }, 51 | }); 52 | // Использование effector-reflect здесь опционально и некритично в рамках методологии 53 | const PageContent = variant({ 54 | source: combine( 55 | { 56 | isLoading: taskModel.$tasksListLoading, 57 | isEmpty: taskModel.$tasksListEmpty, 58 | }, 59 | ({ isLoading, isEmpty }) => { 60 | if (isLoading) return "loading"; 61 | if (isEmpty) return "empty"; 62 | return "ready"; 63 | } 64 | ), 65 | cases: { 66 | loading: () => , 67 | empty: () => , 68 | ready: TasksList, 69 | }, 70 | hooks: { 71 | mounted: taskModel.effects.getTasksListFx.prepend(() => {}), 72 | }, 73 | }); 74 | 75 | export default TasksListPage; 76 | -------------------------------------------------------------------------------- /todo-app/src/pages/tasks-list/styles.module.scss: -------------------------------------------------------------------------------- 1 | .root { 2 | min-height: 100%; 3 | } 4 | 5 | .toolbar { 6 | margin-top: 40px; 7 | } 8 | 9 | .content { 10 | margin: 40px; 11 | } 12 | -------------------------------------------------------------------------------- /todo-app/src/react-app-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /todo-app/src/reportWebVitals.ts: -------------------------------------------------------------------------------- 1 | import { ReportHandler } from 'web-vitals'; 2 | 3 | const reportWebVitals = (onPerfEntry?: ReportHandler) => { 4 | if (onPerfEntry && onPerfEntry instanceof Function) { 5 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 6 | getCLS(onPerfEntry); 7 | getFID(onPerfEntry); 8 | getFCP(onPerfEntry); 9 | getLCP(onPerfEntry); 10 | getTTFB(onPerfEntry); 11 | }); 12 | } 13 | }; 14 | 15 | export default reportWebVitals; 16 | -------------------------------------------------------------------------------- /todo-app/src/setupTests.ts: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /todo-app/src/shared/api/index.ts: -------------------------------------------------------------------------------- 1 | export * as typicodeApi from "./typicode"; 2 | export * from "./models"; 3 | -------------------------------------------------------------------------------- /todo-app/src/shared/api/models.ts: -------------------------------------------------------------------------------- 1 | export * from "./typicode/models"; 2 | -------------------------------------------------------------------------------- /todo-app/src/shared/api/typicode/base.ts: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import { API_URL } from "shared/config"; 3 | 4 | // Потенциально, можно передавать accessToken 5 | export const apiInstance = axios.create({ 6 | baseURL: API_URL 7 | }); 8 | -------------------------------------------------------------------------------- /todo-app/src/shared/api/typicode/index.ts: -------------------------------------------------------------------------------- 1 | export * as tasks from "./tasks"; 2 | -------------------------------------------------------------------------------- /todo-app/src/shared/api/typicode/models.ts: -------------------------------------------------------------------------------- 1 | export type Task = { 2 | id: number; 3 | title: string; 4 | userId: number; 5 | completed: boolean; 6 | }; 7 | -------------------------------------------------------------------------------- /todo-app/src/shared/api/typicode/tasks.ts: -------------------------------------------------------------------------------- 1 | import type { AxiosPromise } from "axios"; 2 | import { apiInstance } from "./base"; 3 | import type { Task } from "./models"; 4 | 5 | const BASE_URL = "/todos" 6 | 7 | export type GetTasksListParams = { 8 | userId?: number; 9 | completed?: boolean; 10 | }; 11 | 12 | export const getTasksList = (params?: GetTasksListParams): AxiosPromise => { 13 | return apiInstance.get(BASE_URL, { params }); 14 | }; 15 | 16 | export type GetTaskByIdParams = { 17 | taskId: number; 18 | [x: string]: any; 19 | }; 20 | 21 | export const getTaskById = ({ taskId, ...params }: GetTaskByIdParams): AxiosPromise => { 22 | return apiInstance.get(`${BASE_URL}/${taskId}`, { params }); 23 | }; 24 | -------------------------------------------------------------------------------- /todo-app/src/shared/config/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Модуль инициализации env-переменных 3 | * @remark Если не найдено значение хоть одной переменной, 4 | * Приложение сразу выбросит ошибку, при инициализации модуля 5 | * @module 6 | */ 7 | 8 | /** 9 | * Получение env-переменной 10 | * @throwable 11 | */ 12 | const getEnvVar = (key: string) => { 13 | if (process.env[key] === undefined) { 14 | throw new Error(`Env variable ${key} is required`); 15 | } 16 | return process.env[key] || ""; 17 | }; 18 | 19 | /** API entrypoint */ 20 | export const API_URL = getEnvVar("REACT_APP_API_URL"); 21 | 22 | /** Режим запуска программы */ 23 | export const NODE_ENV = getEnvVar("NODE_ENV"); 24 | /** Режим разработки */ 25 | export const isDevEnv = NODE_ENV === "development"; 26 | /** Режим продакшена */ 27 | export const isProdEnv = NODE_ENV === "production"; 28 | -------------------------------------------------------------------------------- /todo-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "baseUrl": "./src", 4 | "target": "es5", 5 | "lib": [ 6 | "dom", 7 | "dom.iterable", 8 | "esnext" 9 | ], 10 | "allowJs": true, 11 | "skipLibCheck": true, 12 | "esModuleInterop": true, 13 | "allowSyntheticDefaultImports": true, 14 | "strict": true, 15 | "forceConsistentCasingInFileNames": true, 16 | "noFallthroughCasesInSwitch": true, 17 | "module": "esnext", 18 | "moduleResolution": "node", 19 | "resolveJsonModule": true, 20 | "isolatedModules": true, 21 | "noEmit": true, 22 | "jsx": "react-jsx" 23 | }, 24 | "include": [ 25 | "src" 26 | ] 27 | } 28 | --------------------------------------------------------------------------------