toiljs 0.0.15 → 0.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.babelrc +13 -13
- package/.gitattributes +2 -2
- package/.github/ISSUE_TEMPLATE/bug_report.md +38 -38
- package/.github/ISSUE_TEMPLATE/bug_report.yml +90 -90
- package/.github/ISSUE_TEMPLATE/config.yml +8 -8
- package/.github/ISSUE_TEMPLATE/feature_request.md +20 -20
- package/.github/PULL_REQUEST_TEMPLATE.md +43 -43
- package/.github/changelog-config.json +45 -45
- package/.github/dependabot.yml +27 -27
- package/.github/workflows/ci.yml +191 -191
- package/.prettierrc.json +11 -11
- package/.vscode/settings.json +9 -9
- package/CHANGELOG.md +5 -5
- package/LICENSE +187 -187
- package/README.md +339 -315
- package/as-pect.asconfig.json +34 -34
- package/as-pect.config.js +65 -65
- package/assets/logo.svg +36 -36
- package/build/backend/.tsbuildinfo +1 -1
- package/build/cli/.tsbuildinfo +1 -1
- package/build/cli/index.js +0 -0
- package/build/client/.tsbuildinfo +1 -1
- package/build/client/dev/devtools.d.ts +6 -0
- package/build/client/dev/devtools.js +442 -0
- package/build/client/dev/error-overlay.d.ts +9 -0
- package/build/client/dev/error-overlay.js +19 -4
- package/build/client/navigation/prefetch.d.ts +1 -0
- package/build/client/navigation/prefetch.js +35 -0
- package/build/client/routing/Router.js +1 -1
- package/build/client/routing/hooks.js +6 -2
- package/build/client/routing/loader.d.ts +23 -0
- package/build/client/routing/loader.js +53 -7
- package/build/client/routing/mount.js +4 -3
- package/build/compiler/.tsbuildinfo +1 -1
- package/build/compiler/config.d.ts +16 -0
- package/build/compiler/config.js +7 -0
- package/build/compiler/docs.js +16 -16
- package/build/compiler/index.d.ts +2 -2
- package/build/compiler/index.js +1 -1
- package/build/compiler/plugin.js +156 -0
- package/build/compiler/prerender.d.ts +1 -0
- package/build/compiler/prerender.js +1 -1
- package/build/compiler/seo.d.ts +1 -1
- package/build/compiler/seo.js +5 -4
- package/build/compiler/ssg.js +32 -1
- package/build/io/.tsbuildinfo +1 -1
- package/build/logger/.tsbuildinfo +1 -1
- package/build/shared/.tsbuildinfo +1 -1
- package/eslint.config.js +48 -48
- package/examples/basic/client/404.tsx +11 -11
- package/examples/basic/client/components/.gitkeep +1 -1
- package/examples/basic/client/global-error.tsx +13 -13
- package/examples/basic/client/layout.tsx +25 -25
- package/examples/basic/client/public/images/.gitkeep +1 -1
- package/examples/basic/client/public/images/logo.svg +36 -36
- package/examples/basic/client/public/robots.txt +2 -2
- package/examples/basic/client/routes/docs/[...slug].tsx +12 -12
- package/examples/basic/client/routes/features/error/error.tsx +16 -16
- package/examples/basic/client/routes/features/template/b.tsx +14 -14
- package/examples/basic/client/routes/files/[[...slug]].tsx +21 -21
- package/examples/basic/client/routes/gallery/layout.tsx +13 -13
- package/examples/basic/client/routes/io.tsx +24 -24
- package/examples/basic/client/routes/loader-demo/loading.tsx +13 -13
- package/examples/basic/client/routes/search.tsx +61 -61
- package/examples/basic/client/toil.tsx +5 -5
- package/package.json +155 -148
- package/presets/eslint.js +88 -88
- package/presets/no-uint8array-tostring.js +200 -200
- package/presets/prettier.json +18 -18
- package/presets/tsconfig.json +37 -37
- package/src/backend/index.ts +160 -160
- package/src/cli/proc.ts +50 -50
- package/src/cli/updates.ts +69 -69
- package/src/cli/validate.ts +31 -31
- package/src/client/channel/channel.ts +146 -146
- package/src/client/components/Form.tsx +65 -65
- package/src/client/components/Script.tsx +113 -113
- package/src/client/components/Slot.tsx +21 -21
- package/src/client/dev/devtools.tsx +973 -0
- package/src/client/dev/error-overlay.tsx +30 -4
- package/src/client/head/head.ts +167 -167
- package/src/client/head/metadata.ts +112 -112
- package/src/client/index.ts +89 -89
- package/src/client/navigation/NavLink.tsx +86 -86
- package/src/client/navigation/navigation.ts +235 -235
- package/src/client/navigation/prefetch.ts +169 -130
- package/src/client/navigation/scroll.ts +53 -53
- package/src/client/routing/Router.tsx +8 -2
- package/src/client/routing/action.ts +122 -122
- package/src/client/routing/error-boundary.tsx +43 -43
- package/src/client/routing/hooks.ts +21 -6
- package/src/client/routing/loader.ts +325 -235
- package/src/client/routing/match.ts +47 -47
- package/src/client/routing/mount.tsx +54 -52
- package/src/client/routing/params-context.ts +10 -10
- package/src/client/routing/slot-context.ts +7 -7
- package/src/client/search/search.ts +189 -189
- package/src/client/search/use-page-search.ts +73 -73
- package/src/client/types.ts +73 -73
- package/src/compiler/config.ts +219 -182
- package/src/compiler/docs.ts +228 -228
- package/src/compiler/generate.ts +394 -394
- package/src/compiler/index.ts +64 -57
- package/src/compiler/pages.ts +70 -70
- package/src/compiler/plugin.ts +170 -2
- package/src/compiler/prerender.ts +156 -156
- package/src/compiler/seo.ts +397 -390
- package/src/compiler/ssg.ts +162 -126
- package/src/io/BinaryReader.ts +340 -340
- package/src/io/BinaryWriter.ts +385 -385
- package/src/io/FastMap.ts +127 -127
- package/src/io/index.ts +11 -11
- package/src/io/lengths.ts +14 -14
- package/src/io/types.ts +18 -18
- package/src/logger/index.ts +22 -22
- package/src/server/index.ts +10 -10
- package/src/server/main.ts +13 -13
- package/src/server/tsconfig.json +4 -4
- package/src/shared/index.ts +10 -10
- package/std/client/index.d.ts +15 -15
- package/std/client/package.json +3 -3
- package/test/assembly/example.spec.ts +7 -7
- package/test/channel.test.ts +21 -21
- package/test/dom/Link.test.tsx +47 -47
- package/test/dom/NavLink.test.tsx +37 -37
- package/test/dom/error-overlay.test.tsx +44 -44
- package/test/dom/loader.test.tsx +121 -121
- package/test/dom/navigation.test.ts +59 -59
- package/test/dom/revalidate.test.tsx +38 -38
- package/test/dom/route-head.test.tsx +78 -78
- package/test/dom/router-loading.test.tsx +44 -44
- package/test/dom/scroll.test.ts +56 -56
- package/test/dom/use-metadata.test.tsx +58 -58
- package/test/io.test.ts +93 -93
- package/test/navlink.test.ts +28 -28
- package/test/placeholder.test.ts +9 -9
- package/test/routes.test.ts +76 -76
- package/test/seo.test.ts +175 -164
- package/test/slot-layouts.test.ts +69 -69
- package/test/ssg.test.ts +36 -36
- package/test/update.test.ts +44 -44
- package/test/validate.test.ts +42 -42
- package/toil-routes.d.ts +7 -0
- package/toilconfig.json +30 -30
- package/tsconfig.backend.json +13 -13
- package/tsconfig.base.json +35 -35
- package/tsconfig.cli.json +13 -13
- package/tsconfig.client.json +14 -14
- package/tsconfig.compiler.json +13 -13
- package/tsconfig.io.json +12 -12
- package/tsconfig.json +22 -22
- package/tsconfig.logger.json +12 -12
- package/tsconfig.server.json +10 -10
- package/tsconfig.shared.json +12 -12
- package/vitest.config.ts +26 -26
- package/.idea/codeStyles/Project.xml +0 -54
- package/.idea/codeStyles/codeStyleConfig.xml +0 -5
- package/.idea/inspectionProfiles/Project_Default.xml +0 -6
- package/.idea/modules.xml +0 -8
- package/.idea/prettier.xml +0 -7
- package/.idea/toiljs.iml +0 -8
- package/.idea/vcs.xml +0 -6
- package/.toil/entry.tsx +0 -9
- package/.toil/index.html +0 -12
- package/.toil/routes.ts +0 -9
- package/build/cli/configure.d.ts +0 -16
- package/build/cli/configure.js +0 -272
- package/build/cli/create.d.ts +0 -16
- package/build/cli/create.js +0 -420
- package/build/cli/diagnostics.d.ts +0 -55
- package/build/cli/diagnostics.js +0 -333
- package/build/cli/doctor.d.ts +0 -6
- package/build/cli/doctor.js +0 -249
- package/build/cli/features.d.ts +0 -25
- package/build/cli/features.js +0 -107
- package/build/cli/index.d.ts +0 -2
- package/build/cli/proc.d.ts +0 -6
- package/build/cli/proc.js +0 -31
- package/build/cli/ui.d.ts +0 -9
- package/build/cli/ui.js +0 -75
- package/build/cli/update.d.ts +0 -7
- package/build/cli/update.js +0 -117
- package/build/cli/updates.d.ts +0 -10
- package/build/cli/updates.js +0 -45
- package/build/cli/validate.d.ts +0 -4
- package/build/cli/validate.js +0 -19
- package/build/client/Link.d.ts +0 -8
- package/build/client/Link.js +0 -44
- package/build/client/NavLink.d.ts +0 -14
- package/build/client/NavLink.js +0 -37
- package/build/client/Router.d.ts +0 -7
- package/build/client/Router.js +0 -55
- package/build/client/channel.d.ts +0 -23
- package/build/client/channel.js +0 -94
- package/build/client/error-boundary.d.ts +0 -16
- package/build/client/error-boundary.js +0 -19
- package/build/client/head.d.ts +0 -26
- package/build/client/head.js +0 -87
- package/build/client/hooks.d.ts +0 -17
- package/build/client/hooks.js +0 -48
- package/build/client/lazy.d.ts +0 -16
- package/build/client/lazy.js +0 -53
- package/build/client/match.d.ts +0 -2
- package/build/client/match.js +0 -32
- package/build/client/mount.d.ts +0 -2
- package/build/client/mount.js +0 -13
- package/build/client/navigation.d.ts +0 -13
- package/build/client/navigation.js +0 -97
- package/build/client/params-context.d.ts +0 -2
- package/build/client/params-context.js +0 -2
- package/build/client/prefetch.d.ts +0 -11
- package/build/client/prefetch.js +0 -100
- package/build/client/runtime.d.ts +0 -31
- package/build/client/runtime.js +0 -112
- package/build/client/scroll.d.ts +0 -8
- package/build/client/scroll.js +0 -36
- package/toil-env.d.ts +0 -16
package/presets/eslint.js
CHANGED
|
@@ -1,88 +1,88 @@
|
|
|
1
|
-
/** toiljs shared ESLint flat config: `import toiljs from 'toiljs/eslint'; export default toiljs;` */
|
|
2
|
-
import eslintReact from '@eslint-react/eslint-plugin';
|
|
3
|
-
import eslint from '@eslint/js';
|
|
4
|
-
import reactHooks from 'eslint-plugin-react-hooks';
|
|
5
|
-
import reactRefresh from 'eslint-plugin-react-refresh';
|
|
6
|
-
import tseslint from 'typescript-eslint';
|
|
7
|
-
|
|
8
|
-
import noUint8ArrayToString from './no-uint8array-tostring.js';
|
|
9
|
-
|
|
10
|
-
export default tseslint.config(
|
|
11
|
-
{ ignores: ['dist', 'build', '.toil', 'node_modules', 'toil-env.d.ts', 'server/**'] },
|
|
12
|
-
{
|
|
13
|
-
extends: [
|
|
14
|
-
eslint.configs.recommended,
|
|
15
|
-
...tseslint.configs.recommended,
|
|
16
|
-
...tseslint.configs.strictTypeChecked,
|
|
17
|
-
],
|
|
18
|
-
files: ['**/*.{ts,tsx}'],
|
|
19
|
-
languageOptions: {
|
|
20
|
-
ecmaVersion: 2023,
|
|
21
|
-
parserOptions: {
|
|
22
|
-
projectService: true,
|
|
23
|
-
},
|
|
24
|
-
},
|
|
25
|
-
plugins: {
|
|
26
|
-
'react-hooks': reactHooks,
|
|
27
|
-
'react-refresh': reactRefresh,
|
|
28
|
-
'@eslint-react': eslintReact,
|
|
29
|
-
custom: noUint8ArrayToString,
|
|
30
|
-
},
|
|
31
|
-
rules: {
|
|
32
|
-
...reactHooks.configs.recommended.rules,
|
|
33
|
-
// Route files conventionally export `loader` / `revalidate` / `metadata` /
|
|
34
|
-
// `generateMetadata` / `searchHints` alongside the default component; the toil compiler
|
|
35
|
-
// consumes them at runtime/build. Allow them (plus primitive constants) so Fast Refresh
|
|
36
|
-
// doesn't flag the pattern.
|
|
37
|
-
'react-refresh/only-export-components': [
|
|
38
|
-
'warn',
|
|
39
|
-
{
|
|
40
|
-
allowConstantExport: true,
|
|
41
|
-
allowExportNames: [
|
|
42
|
-
'loader',
|
|
43
|
-
'revalidate',
|
|
44
|
-
'metadata',
|
|
45
|
-
'generateMetadata',
|
|
46
|
-
'searchHints',
|
|
47
|
-
],
|
|
48
|
-
},
|
|
49
|
-
],
|
|
50
|
-
'no-undef': 'off',
|
|
51
|
-
'@typescript-eslint/no-unused-vars': 'off',
|
|
52
|
-
'no-empty': 'off',
|
|
53
|
-
'@typescript-eslint/restrict-template-expressions': 'off',
|
|
54
|
-
'@typescript-eslint/only-throw-error': 'off',
|
|
55
|
-
'@typescript-eslint/no-unnecessary-condition': 'off',
|
|
56
|
-
'@typescript-eslint/unbound-method': 'warn',
|
|
57
|
-
'@typescript-eslint/no-confusing-void-expression': 'off',
|
|
58
|
-
'@typescript-eslint/no-extraneous-class': 'off',
|
|
59
|
-
'no-async-promise-executor': 'off',
|
|
60
|
-
'@typescript-eslint/no-misused-promises': 'off',
|
|
61
|
-
'@typescript-eslint/no-unnecessary-type-parameters': 'off',
|
|
62
|
-
'@typescript-eslint/no-duplicate-enum-values': 'off',
|
|
63
|
-
'prefer-spread': 'off',
|
|
64
|
-
'@typescript-eslint/no-empty-object-type': 'off',
|
|
65
|
-
'@typescript-eslint/no-floating-promises': 'off',
|
|
66
|
-
'@typescript-eslint/ban-ts-comment': 'off',
|
|
67
|
-
'no-constant-binary-expression': 'off',
|
|
68
|
-
'no-useless-assignment': 'off',
|
|
69
|
-
'@typescript-eslint/no-unsafe-assignment': 'off',
|
|
70
|
-
'@typescript-eslint/no-unsafe-call': 'off',
|
|
71
|
-
'@typescript-eslint/no-unsafe-member-access': 'off',
|
|
72
|
-
'@typescript-eslint/no-unsafe-argument': 'off',
|
|
73
|
-
'@typescript-eslint/no-unnecessary-type-conversion': 'warn',
|
|
74
|
-
'react-hooks/set-state-in-effect': 'warn',
|
|
75
|
-
'custom/no-uint8array-tostring': 'error',
|
|
76
|
-
'padding-line-between-statements': [
|
|
77
|
-
'error',
|
|
78
|
-
{ blankLine: 'always', prev: 'block-like', next: '*' },
|
|
79
|
-
],
|
|
80
|
-
'@typescript-eslint/no-deprecated': 'off',
|
|
81
|
-
'@typescript-eslint/no-unnecessary-type-arguments': 'off',
|
|
82
|
-
},
|
|
83
|
-
},
|
|
84
|
-
{
|
|
85
|
-
files: ['**/*.js'],
|
|
86
|
-
...tseslint.configs.disableTypeChecked,
|
|
87
|
-
},
|
|
88
|
-
);
|
|
1
|
+
/** toiljs shared ESLint flat config: `import toiljs from 'toiljs/eslint'; export default toiljs;` */
|
|
2
|
+
import eslintReact from '@eslint-react/eslint-plugin';
|
|
3
|
+
import eslint from '@eslint/js';
|
|
4
|
+
import reactHooks from 'eslint-plugin-react-hooks';
|
|
5
|
+
import reactRefresh from 'eslint-plugin-react-refresh';
|
|
6
|
+
import tseslint from 'typescript-eslint';
|
|
7
|
+
|
|
8
|
+
import noUint8ArrayToString from './no-uint8array-tostring.js';
|
|
9
|
+
|
|
10
|
+
export default tseslint.config(
|
|
11
|
+
{ ignores: ['dist', 'build', '.toil', 'node_modules', 'toil-env.d.ts', 'server/**'] },
|
|
12
|
+
{
|
|
13
|
+
extends: [
|
|
14
|
+
eslint.configs.recommended,
|
|
15
|
+
...tseslint.configs.recommended,
|
|
16
|
+
...tseslint.configs.strictTypeChecked,
|
|
17
|
+
],
|
|
18
|
+
files: ['**/*.{ts,tsx}'],
|
|
19
|
+
languageOptions: {
|
|
20
|
+
ecmaVersion: 2023,
|
|
21
|
+
parserOptions: {
|
|
22
|
+
projectService: true,
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
plugins: {
|
|
26
|
+
'react-hooks': reactHooks,
|
|
27
|
+
'react-refresh': reactRefresh,
|
|
28
|
+
'@eslint-react': eslintReact,
|
|
29
|
+
custom: noUint8ArrayToString,
|
|
30
|
+
},
|
|
31
|
+
rules: {
|
|
32
|
+
...reactHooks.configs.recommended.rules,
|
|
33
|
+
// Route files conventionally export `loader` / `revalidate` / `metadata` /
|
|
34
|
+
// `generateMetadata` / `searchHints` alongside the default component; the toil compiler
|
|
35
|
+
// consumes them at runtime/build. Allow them (plus primitive constants) so Fast Refresh
|
|
36
|
+
// doesn't flag the pattern.
|
|
37
|
+
'react-refresh/only-export-components': [
|
|
38
|
+
'warn',
|
|
39
|
+
{
|
|
40
|
+
allowConstantExport: true,
|
|
41
|
+
allowExportNames: [
|
|
42
|
+
'loader',
|
|
43
|
+
'revalidate',
|
|
44
|
+
'metadata',
|
|
45
|
+
'generateMetadata',
|
|
46
|
+
'searchHints',
|
|
47
|
+
],
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
'no-undef': 'off',
|
|
51
|
+
'@typescript-eslint/no-unused-vars': 'off',
|
|
52
|
+
'no-empty': 'off',
|
|
53
|
+
'@typescript-eslint/restrict-template-expressions': 'off',
|
|
54
|
+
'@typescript-eslint/only-throw-error': 'off',
|
|
55
|
+
'@typescript-eslint/no-unnecessary-condition': 'off',
|
|
56
|
+
'@typescript-eslint/unbound-method': 'warn',
|
|
57
|
+
'@typescript-eslint/no-confusing-void-expression': 'off',
|
|
58
|
+
'@typescript-eslint/no-extraneous-class': 'off',
|
|
59
|
+
'no-async-promise-executor': 'off',
|
|
60
|
+
'@typescript-eslint/no-misused-promises': 'off',
|
|
61
|
+
'@typescript-eslint/no-unnecessary-type-parameters': 'off',
|
|
62
|
+
'@typescript-eslint/no-duplicate-enum-values': 'off',
|
|
63
|
+
'prefer-spread': 'off',
|
|
64
|
+
'@typescript-eslint/no-empty-object-type': 'off',
|
|
65
|
+
'@typescript-eslint/no-floating-promises': 'off',
|
|
66
|
+
'@typescript-eslint/ban-ts-comment': 'off',
|
|
67
|
+
'no-constant-binary-expression': 'off',
|
|
68
|
+
'no-useless-assignment': 'off',
|
|
69
|
+
'@typescript-eslint/no-unsafe-assignment': 'off',
|
|
70
|
+
'@typescript-eslint/no-unsafe-call': 'off',
|
|
71
|
+
'@typescript-eslint/no-unsafe-member-access': 'off',
|
|
72
|
+
'@typescript-eslint/no-unsafe-argument': 'off',
|
|
73
|
+
'@typescript-eslint/no-unnecessary-type-conversion': 'warn',
|
|
74
|
+
'react-hooks/set-state-in-effect': 'warn',
|
|
75
|
+
'custom/no-uint8array-tostring': 'error',
|
|
76
|
+
'padding-line-between-statements': [
|
|
77
|
+
'error',
|
|
78
|
+
{ blankLine: 'always', prev: 'block-like', next: '*' },
|
|
79
|
+
],
|
|
80
|
+
'@typescript-eslint/no-deprecated': 'off',
|
|
81
|
+
'@typescript-eslint/no-unnecessary-type-arguments': 'off',
|
|
82
|
+
},
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
files: ['**/*.js'],
|
|
86
|
+
...tseslint.configs.disableTypeChecked,
|
|
87
|
+
},
|
|
88
|
+
);
|
|
@@ -1,200 +1,200 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ESLint rule: disallow `.toString()` on Uint8Array (and branded byte types), which returns
|
|
3
|
-
* comma-separated decimals instead of hex.
|
|
4
|
-
*/
|
|
5
|
-
import { AST_NODE_TYPES, ESLintUtils } from '@typescript-eslint/utils';
|
|
6
|
-
import { SyntaxKind } from 'typescript';
|
|
7
|
-
|
|
8
|
-
function isUint8ArrayType(type, checker) {
|
|
9
|
-
const symbol = type.getSymbol();
|
|
10
|
-
if (symbol?.getName() === 'Uint8Array') {
|
|
11
|
-
return true;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const baseTypes = type.getBaseTypes?.();
|
|
15
|
-
if (baseTypes) {
|
|
16
|
-
for (const baseType of baseTypes) {
|
|
17
|
-
if (isUint8ArrayType(baseType, checker)) {
|
|
18
|
-
return true;
|
|
19
|
-
}
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
if (type.isIntersection()) {
|
|
24
|
-
for (const subType of type.types) {
|
|
25
|
-
if (isUint8ArrayType(subType, checker)) {
|
|
26
|
-
return true;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
if (type.isUnion()) {
|
|
32
|
-
return (
|
|
33
|
-
type.types.length > 0 &&
|
|
34
|
-
type.types.every((subType) => isUint8ArrayType(subType, checker))
|
|
35
|
-
);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const constraint = type.getConstraint?.();
|
|
39
|
-
if (constraint && isUint8ArrayType(constraint, checker)) {
|
|
40
|
-
return true;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
return false;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/**
|
|
47
|
-
* Types whose toString() is the dangerous default behavior we want to catch.
|
|
48
|
-
* If toString is declared on any type NOT in this set, it has been
|
|
49
|
-
* intentionally overridden and we should leave it alone.
|
|
50
|
-
*/
|
|
51
|
-
const DEFAULT_TOSTRING_OWNERS = new Set([
|
|
52
|
-
'Object',
|
|
53
|
-
'Uint8Array',
|
|
54
|
-
'Int8Array',
|
|
55
|
-
'Uint8ClampedArray',
|
|
56
|
-
'Int16Array',
|
|
57
|
-
'Uint16Array',
|
|
58
|
-
'Int32Array',
|
|
59
|
-
'Uint32Array',
|
|
60
|
-
'Float32Array',
|
|
61
|
-
'Float64Array',
|
|
62
|
-
'BigInt64Array',
|
|
63
|
-
'BigUint64Array',
|
|
64
|
-
]);
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Given a declaration node, walk up the AST parents to find the enclosing
|
|
68
|
-
* class or interface name. More reliable than checker.getTypeAtLocation(decl.parent),
|
|
69
|
-
* which can return odd results for .d.ts files.
|
|
70
|
-
*/
|
|
71
|
-
function getEnclosingClassName(decl) {
|
|
72
|
-
let current = decl.parent;
|
|
73
|
-
while (current) {
|
|
74
|
-
if (
|
|
75
|
-
current.kind === SyntaxKind.ClassDeclaration ||
|
|
76
|
-
current.kind === SyntaxKind.ClassExpression ||
|
|
77
|
-
current.kind === SyntaxKind.InterfaceDeclaration
|
|
78
|
-
) {
|
|
79
|
-
if (current.name) {
|
|
80
|
-
return current.name.text;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
current = current.parent;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
return undefined;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
* Checks whether the resolved toString() on this type is a custom override
|
|
92
|
-
* rather than the default Uint8Array/Object prototype version.
|
|
93
|
-
*/
|
|
94
|
-
function hasCustomToString(type, checker) {
|
|
95
|
-
const toStringSymbol = type.getProperty('toString');
|
|
96
|
-
if (!toStringSymbol) {
|
|
97
|
-
return false;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const declarations = toStringSymbol.getDeclarations();
|
|
101
|
-
if (!declarations || declarations.length === 0) {
|
|
102
|
-
return false;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
for (const decl of declarations) {
|
|
106
|
-
const ownerName = getEnclosingClassName(decl);
|
|
107
|
-
if (ownerName && !DEFAULT_TOSTRING_OWNERS.has(ownerName)) {
|
|
108
|
-
return true;
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const apparentType = checker.getApparentType(type);
|
|
113
|
-
if (apparentType !== type) {
|
|
114
|
-
const apparentToString = apparentType.getProperty('toString');
|
|
115
|
-
if (apparentToString && apparentToString !== toStringSymbol) {
|
|
116
|
-
const apparentDecls = apparentToString.getDeclarations();
|
|
117
|
-
if (apparentDecls) {
|
|
118
|
-
for (const decl of apparentDecls) {
|
|
119
|
-
const ownerName = getEnclosingClassName(decl);
|
|
120
|
-
if (ownerName && !DEFAULT_TOSTRING_OWNERS.has(ownerName)) {
|
|
121
|
-
return true;
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
return false;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
const createRule = ESLintUtils.RuleCreator(
|
|
132
|
-
(name) => `https://github.com/dacely-cloud/toiljs/tree/main/presets#${name}`,
|
|
133
|
-
);
|
|
134
|
-
|
|
135
|
-
const rule = createRule({
|
|
136
|
-
name: 'no-uint8array-tostring',
|
|
137
|
-
meta: {
|
|
138
|
-
type: 'problem',
|
|
139
|
-
docs: {
|
|
140
|
-
description:
|
|
141
|
-
'Disallow .toString() on Uint8Array and branded types (Script, Bytes32, etc.) which produces comma-separated decimals instead of hex',
|
|
142
|
-
},
|
|
143
|
-
messages: {
|
|
144
|
-
noUint8ArrayToString:
|
|
145
|
-
'{{typeName}}.toString() returns comma-separated decimals (e.g. "0,32,70,107"), not a hex string. ' +
|
|
146
|
-
'Use Buffer.from(arr).toString("hex") or toHex() instead.',
|
|
147
|
-
},
|
|
148
|
-
schema: [],
|
|
149
|
-
},
|
|
150
|
-
defaultOptions: [],
|
|
151
|
-
create(context) {
|
|
152
|
-
const services = ESLintUtils.getParserServices(context);
|
|
153
|
-
const checker = services.program.getTypeChecker();
|
|
154
|
-
|
|
155
|
-
return {
|
|
156
|
-
CallExpression(node) {
|
|
157
|
-
if (
|
|
158
|
-
node.callee.type !== AST_NODE_TYPES.MemberExpression ||
|
|
159
|
-
node.callee.property.type !== AST_NODE_TYPES.Identifier ||
|
|
160
|
-
node.callee.property.name !== 'toString' ||
|
|
161
|
-
node.arguments.length > 0
|
|
162
|
-
) {
|
|
163
|
-
return;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const objectNode = node.callee.object;
|
|
167
|
-
const tsNode = services.esTreeNodeToTSNodeMap.get(objectNode);
|
|
168
|
-
const type = checker.getTypeAtLocation(tsNode);
|
|
169
|
-
|
|
170
|
-
if (!isUint8ArrayType(type, checker)) {
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
if (hasCustomToString(type, checker)) {
|
|
175
|
-
return;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const typeName = checker.typeToString(type);
|
|
179
|
-
context.report({
|
|
180
|
-
node,
|
|
181
|
-
messageId: 'noUint8ArrayToString',
|
|
182
|
-
data: { typeName },
|
|
183
|
-
});
|
|
184
|
-
},
|
|
185
|
-
};
|
|
186
|
-
},
|
|
187
|
-
});
|
|
188
|
-
|
|
189
|
-
const plugin = {
|
|
190
|
-
meta: {
|
|
191
|
-
name: 'eslint-plugin-no-uint8array-tostring',
|
|
192
|
-
version: '1.0.0',
|
|
193
|
-
},
|
|
194
|
-
rules: {
|
|
195
|
-
'no-uint8array-tostring': rule,
|
|
196
|
-
},
|
|
197
|
-
};
|
|
198
|
-
|
|
199
|
-
export default plugin;
|
|
200
|
-
export { rule };
|
|
1
|
+
/**
|
|
2
|
+
* ESLint rule: disallow `.toString()` on Uint8Array (and branded byte types), which returns
|
|
3
|
+
* comma-separated decimals instead of hex.
|
|
4
|
+
*/
|
|
5
|
+
import { AST_NODE_TYPES, ESLintUtils } from '@typescript-eslint/utils';
|
|
6
|
+
import { SyntaxKind } from 'typescript';
|
|
7
|
+
|
|
8
|
+
function isUint8ArrayType(type, checker) {
|
|
9
|
+
const symbol = type.getSymbol();
|
|
10
|
+
if (symbol?.getName() === 'Uint8Array') {
|
|
11
|
+
return true;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const baseTypes = type.getBaseTypes?.();
|
|
15
|
+
if (baseTypes) {
|
|
16
|
+
for (const baseType of baseTypes) {
|
|
17
|
+
if (isUint8ArrayType(baseType, checker)) {
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (type.isIntersection()) {
|
|
24
|
+
for (const subType of type.types) {
|
|
25
|
+
if (isUint8ArrayType(subType, checker)) {
|
|
26
|
+
return true;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (type.isUnion()) {
|
|
32
|
+
return (
|
|
33
|
+
type.types.length > 0 &&
|
|
34
|
+
type.types.every((subType) => isUint8ArrayType(subType, checker))
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const constraint = type.getConstraint?.();
|
|
39
|
+
if (constraint && isUint8ArrayType(constraint, checker)) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Types whose toString() is the dangerous default behavior we want to catch.
|
|
48
|
+
* If toString is declared on any type NOT in this set, it has been
|
|
49
|
+
* intentionally overridden and we should leave it alone.
|
|
50
|
+
*/
|
|
51
|
+
const DEFAULT_TOSTRING_OWNERS = new Set([
|
|
52
|
+
'Object',
|
|
53
|
+
'Uint8Array',
|
|
54
|
+
'Int8Array',
|
|
55
|
+
'Uint8ClampedArray',
|
|
56
|
+
'Int16Array',
|
|
57
|
+
'Uint16Array',
|
|
58
|
+
'Int32Array',
|
|
59
|
+
'Uint32Array',
|
|
60
|
+
'Float32Array',
|
|
61
|
+
'Float64Array',
|
|
62
|
+
'BigInt64Array',
|
|
63
|
+
'BigUint64Array',
|
|
64
|
+
]);
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Given a declaration node, walk up the AST parents to find the enclosing
|
|
68
|
+
* class or interface name. More reliable than checker.getTypeAtLocation(decl.parent),
|
|
69
|
+
* which can return odd results for .d.ts files.
|
|
70
|
+
*/
|
|
71
|
+
function getEnclosingClassName(decl) {
|
|
72
|
+
let current = decl.parent;
|
|
73
|
+
while (current) {
|
|
74
|
+
if (
|
|
75
|
+
current.kind === SyntaxKind.ClassDeclaration ||
|
|
76
|
+
current.kind === SyntaxKind.ClassExpression ||
|
|
77
|
+
current.kind === SyntaxKind.InterfaceDeclaration
|
|
78
|
+
) {
|
|
79
|
+
if (current.name) {
|
|
80
|
+
return current.name.text;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
current = current.parent;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return undefined;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Checks whether the resolved toString() on this type is a custom override
|
|
92
|
+
* rather than the default Uint8Array/Object prototype version.
|
|
93
|
+
*/
|
|
94
|
+
function hasCustomToString(type, checker) {
|
|
95
|
+
const toStringSymbol = type.getProperty('toString');
|
|
96
|
+
if (!toStringSymbol) {
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const declarations = toStringSymbol.getDeclarations();
|
|
101
|
+
if (!declarations || declarations.length === 0) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
for (const decl of declarations) {
|
|
106
|
+
const ownerName = getEnclosingClassName(decl);
|
|
107
|
+
if (ownerName && !DEFAULT_TOSTRING_OWNERS.has(ownerName)) {
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const apparentType = checker.getApparentType(type);
|
|
113
|
+
if (apparentType !== type) {
|
|
114
|
+
const apparentToString = apparentType.getProperty('toString');
|
|
115
|
+
if (apparentToString && apparentToString !== toStringSymbol) {
|
|
116
|
+
const apparentDecls = apparentToString.getDeclarations();
|
|
117
|
+
if (apparentDecls) {
|
|
118
|
+
for (const decl of apparentDecls) {
|
|
119
|
+
const ownerName = getEnclosingClassName(decl);
|
|
120
|
+
if (ownerName && !DEFAULT_TOSTRING_OWNERS.has(ownerName)) {
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const createRule = ESLintUtils.RuleCreator(
|
|
132
|
+
(name) => `https://github.com/dacely-cloud/toiljs/tree/main/presets#${name}`,
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
const rule = createRule({
|
|
136
|
+
name: 'no-uint8array-tostring',
|
|
137
|
+
meta: {
|
|
138
|
+
type: 'problem',
|
|
139
|
+
docs: {
|
|
140
|
+
description:
|
|
141
|
+
'Disallow .toString() on Uint8Array and branded types (Script, Bytes32, etc.) which produces comma-separated decimals instead of hex',
|
|
142
|
+
},
|
|
143
|
+
messages: {
|
|
144
|
+
noUint8ArrayToString:
|
|
145
|
+
'{{typeName}}.toString() returns comma-separated decimals (e.g. "0,32,70,107"), not a hex string. ' +
|
|
146
|
+
'Use Buffer.from(arr).toString("hex") or toHex() instead.',
|
|
147
|
+
},
|
|
148
|
+
schema: [],
|
|
149
|
+
},
|
|
150
|
+
defaultOptions: [],
|
|
151
|
+
create(context) {
|
|
152
|
+
const services = ESLintUtils.getParserServices(context);
|
|
153
|
+
const checker = services.program.getTypeChecker();
|
|
154
|
+
|
|
155
|
+
return {
|
|
156
|
+
CallExpression(node) {
|
|
157
|
+
if (
|
|
158
|
+
node.callee.type !== AST_NODE_TYPES.MemberExpression ||
|
|
159
|
+
node.callee.property.type !== AST_NODE_TYPES.Identifier ||
|
|
160
|
+
node.callee.property.name !== 'toString' ||
|
|
161
|
+
node.arguments.length > 0
|
|
162
|
+
) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const objectNode = node.callee.object;
|
|
167
|
+
const tsNode = services.esTreeNodeToTSNodeMap.get(objectNode);
|
|
168
|
+
const type = checker.getTypeAtLocation(tsNode);
|
|
169
|
+
|
|
170
|
+
if (!isUint8ArrayType(type, checker)) {
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
if (hasCustomToString(type, checker)) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const typeName = checker.typeToString(type);
|
|
179
|
+
context.report({
|
|
180
|
+
node,
|
|
181
|
+
messageId: 'noUint8ArrayToString',
|
|
182
|
+
data: { typeName },
|
|
183
|
+
});
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
const plugin = {
|
|
190
|
+
meta: {
|
|
191
|
+
name: 'eslint-plugin-no-uint8array-tostring',
|
|
192
|
+
version: '1.0.0',
|
|
193
|
+
},
|
|
194
|
+
rules: {
|
|
195
|
+
'no-uint8array-tostring': rule,
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
|
|
199
|
+
export default plugin;
|
|
200
|
+
export { rule };
|
package/presets/prettier.json
CHANGED
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
{
|
|
2
|
-
"printWidth": 120,
|
|
3
|
-
"tabWidth": 4,
|
|
4
|
-
"useTabs": false,
|
|
5
|
-
"semi": true,
|
|
6
|
-
"singleQuote": true,
|
|
7
|
-
"trailingComma": "none",
|
|
8
|
-
"bracketSpacing": true,
|
|
9
|
-
"bracketSameLine": true,
|
|
10
|
-
"arrowParens": "always",
|
|
11
|
-
"htmlWhitespaceSensitivity": "css",
|
|
12
|
-
"overrides": [
|
|
13
|
-
{
|
|
14
|
-
"files": "*.html",
|
|
15
|
-
"options": { "parser": "html" }
|
|
16
|
-
}
|
|
17
|
-
]
|
|
18
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"printWidth": 120,
|
|
3
|
+
"tabWidth": 4,
|
|
4
|
+
"useTabs": false,
|
|
5
|
+
"semi": true,
|
|
6
|
+
"singleQuote": true,
|
|
7
|
+
"trailingComma": "none",
|
|
8
|
+
"bracketSpacing": true,
|
|
9
|
+
"bracketSameLine": true,
|
|
10
|
+
"arrowParens": "always",
|
|
11
|
+
"htmlWhitespaceSensitivity": "css",
|
|
12
|
+
"overrides": [
|
|
13
|
+
{
|
|
14
|
+
"files": "*.html",
|
|
15
|
+
"options": { "parser": "html" }
|
|
16
|
+
}
|
|
17
|
+
]
|
|
18
|
+
}
|