├── .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 | 
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 | You need to enable JavaScript to run this app.
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 |
19 |
20 | {props.children}
21 |
22 |
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 |
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 | You need to enable JavaScript to run this app.
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 | Back to tasks list {taskId} }
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 |
--------------------------------------------------------------------------------