@markuplint/svelte-spec 4.5.21 → 4.5.23

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.
@@ -0,0 +1,74 @@
1
+ # @markuplint/svelte-spec
2
+
3
+ ## 概要
4
+
5
+ `@markuplint/svelte-spec` は markuplint 向けの Svelte 固有の拡張仕様を提供します。Svelte のフォーム要素における双方向バインディング動作に対応するため、要素レベルの属性オーバーライドを定義する `ExtendedSpec` オブジェクトをエクスポートします。具体的には、`<select>` と `<textarea>` 要素の `value` 属性の型を `Any` に拡張し、文字列だけでなく任意の型のバインド変数を許容します。
6
+
7
+ ## ExtendedSpec の内容
8
+
9
+ パッケージは `specs` 配列に要素固有のオーバーライドを含む単一の `ExtendedSpec` オブジェクトをエクスポートします:
10
+
11
+ ### 要素固有のオーバーライド
12
+
13
+ | 要素 | 属性 | 型オーバーライド | 理由 |
14
+ | ------------ | ------- | ---------------- | ------------------------------------------------------- |
15
+ | `<select>` | `value` | `Any` | Svelte の `bind:value` は文字列だけでなく任意の型を許容 |
16
+ | `<textarea>` | `value` | `Any` | Svelte の `bind:value` は文字列だけでなく任意の型を許容 |
17
+
18
+ これらのオーバーライドにより、markuplint が Svelte 固有の属性使用を不正としてフラグ付けすることなく、標準 HTML 仕様を拡張します。
19
+
20
+ ## ディレクトリ構成
21
+
22
+ ```
23
+ src/
24
+ └── index.ts — Svelte 固有のオーバーライドを含む ExtendedSpec オブジェクトをエクスポート
25
+ ```
26
+
27
+ ## 主要ソースファイル
28
+
29
+ | ファイル | 用途 |
30
+ | -------------- | ----------------------------------------------------------- |
31
+ | `src/index.ts` | Svelte 用の `ExtendedSpec` オブジェクトを定義・エクスポート |
32
+
33
+ ## 統合ポイント
34
+
35
+ ```mermaid
36
+ flowchart TD
37
+ subgraph upstream ["上流"]
38
+ htmlSpec["@markuplint/html-spec\n(ベース HTML 仕様)"]
39
+ mlSpec["@markuplint/ml-spec\n(ExtendedSpec 型)"]
40
+ end
41
+
42
+ subgraph pkg ["@markuplint/svelte-spec"]
43
+ spec["ExtendedSpec オブジェクト\n(select, textarea オーバーライド)"]
44
+ end
45
+
46
+ subgraph downstream ["下流"]
47
+ mlCore["@markuplint/ml-core\n(schemaToSpec で仕様をマージ)"]
48
+ end
49
+
50
+ subgraph paired ["対となるパーサー"]
51
+ svelteParser["@markuplint/svelte-parser\n(Svelte コンポーネントのパース)"]
52
+ end
53
+
54
+ mlSpec -->|"ExtendedSpec 型"| spec
55
+ htmlSpec -->|"ベース要素定義"| mlCore
56
+ spec -->|"Svelte オーバーライド"| mlCore
57
+ svelteParser -->|"MLASTDocument を生成"| mlCore
58
+ ```
59
+
60
+ ### 上流
61
+
62
+ - **`@markuplint/ml-spec`** -- このパッケージが実装する `ExtendedSpec` 型定義を提供
63
+
64
+ ### 下流
65
+
66
+ - **`@markuplint/ml-core`** -- `schemaToSpec()` を通じてこの仕様を利用し、Svelte のオーバーライドをベース HTML 仕様にマージ
67
+
68
+ ### 対となるパーサー
69
+
70
+ - **`@markuplint/svelte-parser`** -- Svelte コンポーネント構文を処理するパーサー。パーサーが Svelte テンプレートを markuplint AST に変換する一方、この spec パッケージはリンティングに必要な属性型情報を提供します。
71
+
72
+ ## ドキュメントマップ
73
+
74
+ - [メンテナンスガイド](docs/maintenance.ja.md) -- コマンド、レシピ、型リファレンス
@@ -0,0 +1,74 @@
1
+ # @markuplint/svelte-spec
2
+
3
+ ## Overview
4
+
5
+ `@markuplint/svelte-spec` provides Svelte-specific extended specifications for markuplint. It exports an `ExtendedSpec` object that defines element-level attribute overrides to accommodate Svelte's two-way binding behavior on form elements. Specifically, the `<select>` and `<textarea>` elements have their `value` attribute type broadened to `Any`, allowing bound variables of any type rather than only strings.
6
+
7
+ ## ExtendedSpec Content
8
+
9
+ The package exports a single `ExtendedSpec` object with element-specific overrides in the `specs` array:
10
+
11
+ ### Element-Specific Overrides
12
+
13
+ | Element | Attribute | Type Override | Reason |
14
+ | ------------ | --------- | ------------- | ------------------------------------------------------- |
15
+ | `<select>` | `value` | `Any` | Svelte's `bind:value` allows any type, not just strings |
16
+ | `<textarea>` | `value` | `Any` | Svelte's `bind:value` allows any type, not just strings |
17
+
18
+ These overrides extend the standard HTML spec so that markuplint does not flag Svelte-specific attribute usage as invalid.
19
+
20
+ ## Directory Structure
21
+
22
+ ```
23
+ src/
24
+ └── index.ts — Exports the ExtendedSpec object with Svelte-specific overrides
25
+ ```
26
+
27
+ ## Key Source Files
28
+
29
+ | File | Purpose |
30
+ | -------------- | -------------------------------------------------------- |
31
+ | `src/index.ts` | Defines and exports the `ExtendedSpec` object for Svelte |
32
+
33
+ ## Integration Points
34
+
35
+ ```mermaid
36
+ flowchart TD
37
+ subgraph upstream ["Upstream"]
38
+ htmlSpec["@markuplint/html-spec\n(Base HTML spec)"]
39
+ mlSpec["@markuplint/ml-spec\n(ExtendedSpec type)"]
40
+ end
41
+
42
+ subgraph pkg ["@markuplint/svelte-spec"]
43
+ spec["ExtendedSpec object\n(select, textarea overrides)"]
44
+ end
45
+
46
+ subgraph downstream ["Downstream"]
47
+ mlCore["@markuplint/ml-core\n(Merges specs via schemaToSpec)"]
48
+ end
49
+
50
+ subgraph paired ["Paired Parser"]
51
+ svelteParser["@markuplint/svelte-parser\n(Svelte component parsing)"]
52
+ end
53
+
54
+ mlSpec -->|"ExtendedSpec type"| spec
55
+ htmlSpec -->|"Base element definitions"| mlCore
56
+ spec -->|"Svelte overrides"| mlCore
57
+ svelteParser -->|"Produces MLASTDocument"| mlCore
58
+ ```
59
+
60
+ ### Upstream
61
+
62
+ - **`@markuplint/ml-spec`** -- Provides the `ExtendedSpec` type definition that this package implements
63
+
64
+ ### Downstream
65
+
66
+ - **`@markuplint/ml-core`** -- Consumes this spec via `schemaToSpec()`, merging Svelte overrides with the base HTML spec
67
+
68
+ ### Paired Parser
69
+
70
+ - **`@markuplint/svelte-parser`** -- The parser counterpart that handles Svelte component syntax. While the parser converts Svelte templates into the markuplint AST, this spec package provides the attribute type information needed for linting.
71
+
72
+ ## Documentation Map
73
+
74
+ - [Maintenance Guide](docs/maintenance.md) -- Commands, recipes, and type reference
package/CHANGELOG.md CHANGED
@@ -3,13 +3,17 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
- ## [4.5.21](https://github.com/markuplint/markuplint/compare/@markuplint/svelte-spec@4.5.20...@markuplint/svelte-spec@4.5.21) (2025-08-24)
6
+ ## [4.5.23](https://github.com/markuplint/markuplint/compare/@markuplint/svelte-spec@4.5.22...@markuplint/svelte-spec@4.5.23) (2026-02-10)
7
7
 
8
8
  **Note:** Version bump only for package @markuplint/svelte-spec
9
9
 
10
+ ## [4.5.22](https://github.com/markuplint/markuplint/compare/@markuplint/svelte-spec@4.5.21...@markuplint/svelte-spec@4.5.22) (2025-11-05)
10
11
 
12
+ **Note:** Version bump only for package @markuplint/svelte-spec
11
13
 
14
+ ## [4.5.21](https://github.com/markuplint/markuplint/compare/@markuplint/svelte-spec@4.5.20...@markuplint/svelte-spec@4.5.21) (2025-08-24)
12
15
 
16
+ **Note:** Version bump only for package @markuplint/svelte-spec
13
17
 
14
18
  ## [4.5.20](https://github.com/markuplint/markuplint/compare/@markuplint/svelte-spec@4.5.19...@markuplint/svelte-spec@4.5.20) (2025-08-13)
15
19
 
package/SKILL.md ADDED
@@ -0,0 +1,99 @@
1
+ ---
2
+ description: Maintenance tasks for @markuplint/svelte-spec
3
+ globs:
4
+ - packages/@markuplint/svelte-spec/src/**/*.ts
5
+ alwaysApply: false
6
+ ---
7
+
8
+ # svelte-spec-maintenance
9
+
10
+ Perform maintenance tasks for `@markuplint/svelte-spec`: add global attributes,
11
+ add element-specific attribute overrides for Svelte components.
12
+
13
+ ## Input
14
+
15
+ `$ARGUMENTS` specifies the task. Supported tasks:
16
+
17
+ | Task | Description |
18
+ | ---------------------- | ------------------------------------------ |
19
+ | `add-global-attribute` | Add a new global attribute for Svelte |
20
+ | `add-element-override` | Add an element-specific attribute override |
21
+
22
+ If omitted, defaults to `add-element-override`.
23
+
24
+ ## Reference
25
+
26
+ Before executing any task, read `docs/maintenance.md` (or `docs/maintenance.ja.md`)
27
+ for the full guide. The recipes there are the source of truth for procedures.
28
+
29
+ Also read:
30
+
31
+ - `ARCHITECTURE.md` -- Package overview, ExtendedSpec content, and integration points
32
+ - `src/index.ts` -- The single source file (source of truth for the spec object)
33
+
34
+ ## Task: add-global-attribute
35
+
36
+ Add a new global attribute that applies to all Svelte elements. Follow recipe #1 in `docs/maintenance.md`.
37
+
38
+ ### Step 1: Read the current spec
39
+
40
+ 1. Read `src/index.ts` to understand the current `ExtendedSpec` structure
41
+ 2. Check if a `def` property with `#globalAttrs` already exists
42
+
43
+ ### Step 2: Add the global attribute
44
+
45
+ 1. If `def` does not exist, add it to the spec object:
46
+ ```ts
47
+ def: {
48
+ '#globalAttrs': {
49
+ '#extends': {
50
+ 'attribute-name': {
51
+ type: 'Any',
52
+ },
53
+ },
54
+ },
55
+ },
56
+ ```
57
+ 2. If `def['#globalAttrs']['#extends']` already exists, add the new attribute to it
58
+
59
+ ### Step 3: Verify
60
+
61
+ 1. Build: `yarn build --scope @markuplint/svelte-spec`
62
+ 2. Test: `yarn test --scope @markuplint/svelte-spec`
63
+
64
+ ## Task: add-element-override
65
+
66
+ Add an element-specific attribute override. Follow recipe #2 in `docs/maintenance.md`.
67
+
68
+ ### Step 1: Read the current spec
69
+
70
+ 1. Read `src/index.ts` to understand the current `specs` array
71
+ 2. Check if the target element already has an entry
72
+
73
+ ### Step 2: Add the override
74
+
75
+ 1. If the element already exists in `specs`, add the new attribute to its `attributes` object
76
+ 2. If the element does not exist, add a new entry to the `specs` array:
77
+ ```ts
78
+ {
79
+ name: 'element-name',
80
+ attributes: {
81
+ 'attribute-name': {
82
+ type: 'Any',
83
+ },
84
+ },
85
+ },
86
+ ```
87
+
88
+ ### Step 3: Verify
89
+
90
+ 1. Build: `yarn build --scope @markuplint/svelte-spec`
91
+ 2. Test: `yarn test --scope @markuplint/svelte-spec`
92
+
93
+ ## Rules
94
+
95
+ 1. **Only export an `ExtendedSpec` object** -- this package must not contain any parsing logic.
96
+ 2. **Global attributes go under `def['#globalAttrs']['#extends']`** -- not in the `specs` array.
97
+ 3. **Element overrides go under the `specs[]` array** -- each entry needs a `name` and `attributes` object.
98
+ 4. **Each attribute needs at minimum a `type` field** -- common values are `Any`, `String`, `Boolean`, or an enum object.
99
+ 5. **Add JSDoc comments** to any new attribute definitions explaining why the override is needed.
@@ -0,0 +1,127 @@
1
+ # メンテナンスガイド
2
+
3
+ ## コマンド
4
+
5
+ | コマンド | 説明 |
6
+ | -------------------------------------------- | ---------------------- |
7
+ | `yarn build --scope @markuplint/svelte-spec` | このパッケージをビルド |
8
+ | `yarn dev --scope @markuplint/svelte-spec` | ウォッチモードでビルド |
9
+ | `yarn clean --scope @markuplint/svelte-spec` | ビルド成果物を削除 |
10
+ | `yarn test --scope @markuplint/svelte-spec` | テストを実行 |
11
+
12
+ ## テスト
13
+
14
+ このパッケージは静的なデータオブジェクトのみをエクスポートするため、デフォルトではテストファイルがありません。検証はビルドステップで行われます(TypeScript の型チェックにより、エクスポートされたオブジェクトが `ExtendedSpec` 型に準拠していることが保証されます)。
15
+
16
+ 下流パッケージとの統合を検証するには:
17
+
18
+ ```shell
19
+ yarn test --scope @markuplint/svelte-parser
20
+ ```
21
+
22
+ ## レシピ
23
+
24
+ ### 1. グローバル属性の追加
25
+
26
+ 1. `src/index.ts` を読む
27
+ 2. spec オブジェクトに `def` プロパティが存在しない場合、追加する:
28
+ ```ts
29
+ const spec: ExtendedSpec = {
30
+ def: {
31
+ '#globalAttrs': {
32
+ '#extends': {
33
+ 'new-attribute': {
34
+ type: 'Any',
35
+ },
36
+ },
37
+ },
38
+ },
39
+ specs: [
40
+ // 既存のエントリ...
41
+ ],
42
+ };
43
+ ```
44
+ 3. `def['#globalAttrs']['#extends']` が既に存在する場合、既存のオブジェクトに新しい属性を追加
45
+ 4. ビルド: `yarn build --scope @markuplint/svelte-spec`
46
+ 5. 下流パーサーが正常に動作することを確認: `yarn test --scope @markuplint/svelte-parser`
47
+
48
+ ### 2. 要素固有のオーバーライドの追加
49
+
50
+ 1. `src/index.ts` を読む
51
+ 2. 対象の要素が `specs` 配列に既にエントリを持っているか確認
52
+ 3. 要素が存在する場合、その `attributes` オブジェクトに新しい属性を追加:
53
+ ```ts
54
+ {
55
+ name: 'existing-element',
56
+ attributes: {
57
+ existingAttr: { type: 'Any' },
58
+ newAttr: { type: 'Any' }, // ここに追加
59
+ },
60
+ },
61
+ ```
62
+ 4. 要素が存在しない場合、`specs` 配列に新しいエントリを追加:
63
+ ```ts
64
+ {
65
+ name: 'new-element',
66
+ attributes: {
67
+ 'attribute-name': {
68
+ type: 'Any',
69
+ },
70
+ },
71
+ },
72
+ ```
73
+ 5. ビルド: `yarn build --scope @markuplint/svelte-spec`
74
+ 6. 下流パーサーが正常に動作することを確認: `yarn test --scope @markuplint/svelte-parser`
75
+
76
+ ## ExtendedSpec 型リファレンス
77
+
78
+ `ExtendedSpec` 型(`@markuplint/ml-spec` から提供)は以下の構造を持ちます:
79
+
80
+ ```ts
81
+ type ExtendedSpec = {
82
+ readonly cites?: Cites; // 参照 URL
83
+ readonly def?: Partial<SpecDefs>; // グローバル定義
84
+ readonly specs?: readonly ExtendedElementSpec[]; // 要素ごとのオーバーライド
85
+ };
86
+ ```
87
+
88
+ ### `def` -- グローバル定義
89
+
90
+ すべての要素に適用される属性に使用:
91
+
92
+ ```ts
93
+ def: {
94
+ '#globalAttrs': {
95
+ '#extends': {
96
+ 'attribute-name': {
97
+ type: 'Any',
98
+ },
99
+ },
100
+ },
101
+ }
102
+ ```
103
+
104
+ ### `specs` -- 要素ごとのオーバーライド
105
+
106
+ `specs` 配列の各エントリは特定の HTML 要素を対象とします:
107
+
108
+ ```ts
109
+ specs: [
110
+ {
111
+ name: 'element-name', // HTML タグ名
112
+ attributes: {
113
+ 'attribute-name': {
114
+ type: 'Any', // 型オーバーライド
115
+ },
116
+ },
117
+ },
118
+ ];
119
+ ```
120
+
121
+ よく使われる属性型の値:
122
+
123
+ | 型の値 | 意味 |
124
+ | ----------- | ---------------------------------- |
125
+ | `'Any'` | 任意の型を許容(バインド値に使用) |
126
+ | `'String'` | 文字列値のみ許容 |
127
+ | `'Boolean'` | ブール属性(存在/不在) |
@@ -0,0 +1,127 @@
1
+ # Maintenance Guide
2
+
3
+ ## Commands
4
+
5
+ | Command | Description |
6
+ | -------------------------------------------- | ---------------------- |
7
+ | `yarn build --scope @markuplint/svelte-spec` | Build this package |
8
+ | `yarn dev --scope @markuplint/svelte-spec` | Watch mode build |
9
+ | `yarn clean --scope @markuplint/svelte-spec` | Remove build artifacts |
10
+ | `yarn test --scope @markuplint/svelte-spec` | Run tests |
11
+
12
+ ## Testing
13
+
14
+ This package has no test files by default because it exports only a static data object. Verification is done through the build step (TypeScript type checking ensures the exported object conforms to the `ExtendedSpec` type).
15
+
16
+ To verify integration with downstream packages:
17
+
18
+ ```shell
19
+ yarn test --scope @markuplint/svelte-parser
20
+ ```
21
+
22
+ ## Recipes
23
+
24
+ ### 1. Adding a Global Attribute
25
+
26
+ 1. Read `src/index.ts`
27
+ 2. If the `def` property does not exist on the spec object, add it:
28
+ ```ts
29
+ const spec: ExtendedSpec = {
30
+ def: {
31
+ '#globalAttrs': {
32
+ '#extends': {
33
+ 'new-attribute': {
34
+ type: 'Any',
35
+ },
36
+ },
37
+ },
38
+ },
39
+ specs: [
40
+ // existing entries...
41
+ ],
42
+ };
43
+ ```
44
+ 3. If `def['#globalAttrs']['#extends']` already exists, add the new attribute to the existing object
45
+ 4. Build: `yarn build --scope @markuplint/svelte-spec`
46
+ 5. Verify the downstream parser still works: `yarn test --scope @markuplint/svelte-parser`
47
+
48
+ ### 2. Adding an Element-Specific Override
49
+
50
+ 1. Read `src/index.ts`
51
+ 2. Check if the target element already has an entry in the `specs` array
52
+ 3. If the element exists, add the new attribute to its `attributes` object:
53
+ ```ts
54
+ {
55
+ name: 'existing-element',
56
+ attributes: {
57
+ existingAttr: { type: 'Any' },
58
+ newAttr: { type: 'Any' }, // add here
59
+ },
60
+ },
61
+ ```
62
+ 4. If the element does not exist, add a new entry to the `specs` array:
63
+ ```ts
64
+ {
65
+ name: 'new-element',
66
+ attributes: {
67
+ 'attribute-name': {
68
+ type: 'Any',
69
+ },
70
+ },
71
+ },
72
+ ```
73
+ 5. Build: `yarn build --scope @markuplint/svelte-spec`
74
+ 6. Verify the downstream parser still works: `yarn test --scope @markuplint/svelte-parser`
75
+
76
+ ## ExtendedSpec Type Reference
77
+
78
+ The `ExtendedSpec` type (from `@markuplint/ml-spec`) has the following structure:
79
+
80
+ ```ts
81
+ type ExtendedSpec = {
82
+ readonly cites?: Cites; // Reference URLs
83
+ readonly def?: Partial<SpecDefs>; // Global definitions
84
+ readonly specs?: readonly ExtendedElementSpec[]; // Per-element overrides
85
+ };
86
+ ```
87
+
88
+ ### `def` -- Global Definitions
89
+
90
+ Used for attributes that apply to all elements:
91
+
92
+ ```ts
93
+ def: {
94
+ '#globalAttrs': {
95
+ '#extends': {
96
+ 'attribute-name': {
97
+ type: 'Any',
98
+ },
99
+ },
100
+ },
101
+ }
102
+ ```
103
+
104
+ ### `specs` -- Per-Element Overrides
105
+
106
+ Each entry in the `specs` array targets a specific HTML element:
107
+
108
+ ```ts
109
+ specs: [
110
+ {
111
+ name: 'element-name', // HTML tag name
112
+ attributes: {
113
+ 'attribute-name': {
114
+ type: 'Any', // Type override
115
+ },
116
+ },
117
+ },
118
+ ];
119
+ ```
120
+
121
+ Common attribute type values:
122
+
123
+ | Type Value | Meaning |
124
+ | ----------- | ---------------------------------------- |
125
+ | `'Any'` | Accepts any type (used for bound values) |
126
+ | `'String'` | Accepts only string values |
127
+ | `'Boolean'` | Boolean attribute (presence/absence) |
package/lib/index.d.ts CHANGED
@@ -1,3 +1,20 @@
1
+ /**
2
+ * @module @markuplint/svelte-spec
3
+ *
4
+ * Provides Svelte-specific extended specifications for markuplint.
5
+ * Defines element-level attribute overrides for Svelte's two-way
6
+ * binding behavior on form elements (`<select>` and `<textarea>`),
7
+ * where the `value` attribute accepts any type to support bound
8
+ * variables.
9
+ */
1
10
  import type { ExtendedSpec } from '@markuplint/ml-spec';
11
+ /**
12
+ * The Svelte framework extended specification.
13
+ *
14
+ * Provides per-element attribute definitions that accommodate
15
+ * Svelte's two-way binding (`bind:value`) on `<select>` and
16
+ * `<textarea>` elements, allowing the `value` attribute to accept
17
+ * any type rather than only strings.
18
+ */
2
19
  declare const spec: ExtendedSpec;
3
20
  export default spec;
package/lib/index.js CHANGED
@@ -1,3 +1,20 @@
1
+ /**
2
+ * @module @markuplint/svelte-spec
3
+ *
4
+ * Provides Svelte-specific extended specifications for markuplint.
5
+ * Defines element-level attribute overrides for Svelte's two-way
6
+ * binding behavior on form elements (`<select>` and `<textarea>`),
7
+ * where the `value` attribute accepts any type to support bound
8
+ * variables.
9
+ */
10
+ /**
11
+ * The Svelte framework extended specification.
12
+ *
13
+ * Provides per-element attribute definitions that accommodate
14
+ * Svelte's two-way binding (`bind:value`) on `<select>` and
15
+ * `<textarea>` elements, allowing the `value` attribute to accept
16
+ * any type rather than only strings.
17
+ */
1
18
  const spec = {
2
19
  specs: [
3
20
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@markuplint/svelte-spec",
3
- "version": "4.5.21",
3
+ "version": "4.5.23",
4
4
  "description": "Extended specification for tags and attributes in Svelte",
5
5
  "repository": "git@github.com:markuplint/markuplint.git",
6
6
  "author": "Yusuke Hirao <yusukehirao@me.com>",
@@ -21,7 +21,7 @@
21
21
  "clean": "tsc --build --clean tsconfig.build.json"
22
22
  },
23
23
  "dependencies": {
24
- "@markuplint/ml-spec": "4.10.0"
24
+ "@markuplint/ml-spec": "4.10.2"
25
25
  },
26
- "gitHead": "ae97eb2d31ecedf4f0800fbbf18588aad4ebca04"
26
+ "gitHead": "193ee7c1262bbed95424e38efdf1a8e56ff049f4"
27
27
  }