Repository: sverweij/dependency-cruiser
Pull request #948 (2024-07-16 10:23:45)

feat: support svelte 4
## Description
This pull request adds support for Svelte version 4. While Svelte's next version will be 5, this pull request provides support up to version 4.

## Motivation and Context
Dependency Cruiser does not currently support the latest version of Svelte, which is version 4. This update aims to address that. The syntax between Svelte 3 and 4 has not fundamentally changed, so only minimal modifications were needed to support version 4.

https://svelte.dev/docs/v4-migration-guide

## How Has This Been Tested?

<!--- Please describe in detail how you tested your changes. -->
<!--- Include details of your testing environment, and the tests you ran to -->
<!--- see how your change affects other areas of the code, etc. -->

- [ ] green ci

## Screenshots

<!-- Only if appropriate - feel free to delete this section if it's not applicable -->

## Types of changes

<!--- What types of changes does your code introduce? Put an `x` in all the boxes that apply: -->

- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] Documentation only change
- [ ] Refactor (non-breaking change which fixes an issue without changing functionality)
- [x] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)

## Checklist

- [x] :book:
  - it _does_ and I have updated it

- [x] :balance_scale:
  - The contribution will be subject to [The MIT license](https://github.com/sverweij/dependency-cruiser/blob/main/LICENSE), and I'm OK with that.
  - The contribution is my own original work.
  - I am ok with the stuff in [**CONTRIBUTING.md**](https://github.com/sverweij/dependency-cruiser/blob/main/.github/CONTRIBUTING.md).


codeclimate[bot]: Code Climate has analyzed commit fa02fe04 and detected **0 issues** on this pull request.

View more on [Code Climate](https://codeclimate.com/github/sverweij/dependency-cruiser/pull/948).

socket-security[bot]: <!-- overview-comment -->
**New and removed dependencies detected.** Learn more about [Socket for GitHub ↗︎](https://socket.dev?utm_medium=gh)


| Package | New capabilities | Transitives | Size | Publisher |
|:--- |:--- |:--- |:--- |:--- |
| [npm/svelte@4.2.18](https://socket.dev/npm/package/svelte/overview/4.2.18) | Transitive: <a href="https://socket.dev/npm/package/svelte/alerts/4.2.18?tab=dependencies">unsafe</a> | <a href="https://socket.dev/npm/package/svelte/dependencies/4.2.18">`+19`</a> | 5.9 MB | <a href="https://socket.dev/npm/user/svelte-admin">svelte-admin</a> |


**🚮 Removed packages:** [npm/svelte@3.59.1](https://socket.dev/npm/package/svelte/overview/3.59.1)


[**View full report↗︎**](https://socket.dev/dashboard/org/sverweij/diff/5f5f81cb-94e8-4e29-87f5-0a09981804d4/f1c6c441-f1e1-4225-8f84-96093edc86ac)
codeclimate[bot]: Code Climate has analyzed commit 759577da and detected **0 issues** on this pull request.

View more on [Code Climate](https://codeclimate.com/github/sverweij/dependency-cruiser/pull/948).

sverweij: Hi @kubotak-is thanks for taking the time to research this and turning it in to a pull request! I was surprised adding support for svelte 4 looks so relatively straightforward.

> Apparently an update to eslint and some caching crossed each other, resulting in a
> linting error that got nothing to do with your PR, sorry about that!
> I've added a commit (759577d2) to set that straight.

Patch:
diff --git a/doc/faq.md b/doc/faq.md
index ffa6d99e..feb4dc99 100644
--- a/doc/faq.md
+++ b/doc/faq.md
@@ -404,7 +404,7 @@ manually.
 <details>
 <summary>Background</summary>
 
-For `.svelte` single file components it uses the `svelte` (version 3.x)
+For `.svelte` single file components it uses the `svelte` (version 4.x)
 
 - which will be in your module dependencies if you're developing with Svelte.
 - because of how svelte works, all `.svelte` files depend on `"svelte/internal"`.
diff --git a/package-lock.json b/package-lock.json
index 5209c9d5..17afc5dc 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -72,7 +72,7 @@
         "npm-run-all": "4.1.5",
         "prettier": "3.3.3",
         "proxyquire": "2.1.3",
-        "svelte": "3.59.1",
+        "svelte": "4.2.18",
         "symlink-dir": "6.0.1",
         "typescript": "5.5.3",
         "upem": "9.0.4",
@@ -1202,6 +1202,12 @@
         "@swc/counter": "^0.1.3"
       }
     },
+    "node_modules/@types/estree": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.5.tgz",
+      "integrity": "sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==",
+      "dev": true
+    },
     "node_modules/@types/istanbul-lib-coverage": {
       "version": "2.0.4",
       "dev": true,
@@ -1666,6 +1672,15 @@
       "dev": true,
       "license": "Python-2.0"
     },
+    "node_modules/aria-query": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz",
+      "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==",
+      "dev": true,
+      "dependencies": {
+        "dequal": "^2.0.3"
+      }
+    },
     "node_modules/array-buffer-byte-length": {
       "version": "1.0.0",
       "dev": true,
@@ -1794,6 +1809,15 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/axobject-query": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.0.0.tgz",
+      "integrity": "sha512-+60uv1hiVFhHZeO+Lz0RYzsVHy5Wr1ayX0mwda9KPDVLNJgZ1T9Ny7VmFbLDzxsH0D87I86vgj3gFrjTJUYznw==",
+      "dev": true,
+      "dependencies": {
+        "dequal": "^2.0.3"
+      }
+    },
     "node_modules/balanced-match": {
       "version": "1.0.2",
       "dev": true,
@@ -2303,6 +2327,28 @@
         "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
       }
     },
+    "node_modules/code-red": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/code-red/-/code-red-1.0.4.tgz",
+      "integrity": "sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==",
+      "dev": true,
+      "dependencies": {
+        "@jridgewell/sourcemap-codec": "^1.4.15",
+        "@types/estree": "^1.0.1",
+        "acorn": "^8.10.0",
+        "estree-walker": "^3.0.3",
+        "periscopic": "^3.1.0"
+      }
+    },
+    "node_modules/code-red/node_modules/estree-walker": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
+      "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+      "dev": true,
+      "dependencies": {
+        "@types/estree": "^1.0.0"
+      }
+    },
     "node_modules/coffeescript": {
       "version": "2.7.0",
       "dev": true,
@@ -2378,6 +2424,19 @@
         "node": ">= 8"
       }
     },
+    "node_modules/css-tree": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-2.3.1.tgz",
+      "integrity": "sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==",
+      "dev": true,
+      "dependencies": {
+        "mdn-data": "2.0.30",
+        "source-map-js": "^1.0.1"
+      },
+      "engines": {
+        "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0"
+      }
+    },
     "node_modules/de-indent": {
       "version": "1.0.2",
       "dev": true,
@@ -2446,6 +2505,15 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/dequal": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
+      "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
     "node_modules/diff": {
       "version": "5.2.0",
       "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz",
@@ -4249,6 +4317,15 @@
         "node": ">=8"
       }
     },
+    "node_modules/is-reference": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/is-reference/-/is-reference-3.0.2.tgz",
+      "integrity": "sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==",
+      "dev": true,
+      "dependencies": {
+        "@types/estree": "*"
+      }
+    },
     "node_modules/is-regex": {
       "version": "1.1.4",
       "dev": true,
@@ -4650,6 +4727,12 @@
         "node": ">=4"
       }
     },
+    "node_modules/locate-character": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/locate-character/-/locate-character-3.0.0.tgz",
+      "integrity": "sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==",
+      "dev": true
+    },
     "node_modules/locate-path": {
       "version": "6.0.0",
       "dev": true,
@@ -4867,6 +4950,12 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/mdn-data": {
+      "version": "2.0.30",
+      "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.30.tgz",
+      "integrity": "sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==",
+      "dev": true
+    },
     "node_modules/memoize": {
       "version": "10.0.0",
       "resolved": "https://registry.npmjs.org/memoize/-/memoize-10.0.0.tgz",
@@ -5532,6 +5621,26 @@
         "node": ">=8"
       }
     },
+    "node_modules/periscopic": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz",
+      "integrity": "sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==",
+      "dev": true,
+      "dependencies": {
+        "@types/estree": "^1.0.0",
+        "estree-walker": "^3.0.0",
+        "is-reference": "^3.0.0"
+      }
+    },
+    "node_modules/periscopic/node_modules/estree-walker": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
+      "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+      "dev": true,
+      "dependencies": {
+        "@types/estree": "^1.0.0"
+      }
+    },
     "node_modules/picocolors": {
       "version": "1.0.1",
       "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.1.tgz",
@@ -6564,11 +6673,37 @@
       }
     },
     "node_modules/svelte": {
-      "version": "3.59.1",
+      "version": "4.2.18",
+      "resolved": "https://registry.npmjs.org/svelte/-/svelte-4.2.18.tgz",
+      "integrity": "sha512-d0FdzYIiAePqRJEb90WlJDkjUEx42xhivxN8muUBmfZnP+tzUgz12DJ2hRJi8sIHCME7jeK1PTMgKPSfTd8JrA==",
       "dev": true,
-      "license": "MIT",
+      "dependencies": {
+        "@ampproject/remapping": "^2.2.1",
+        "@jridgewell/sourcemap-codec": "^1.4.15",
+        "@jridgewell/trace-mapping": "^0.3.18",
+        "@types/estree": "^1.0.1",
+        "acorn": "^8.9.0",
+        "aria-query": "^5.3.0",
+        "axobject-query": "^4.0.0",
+        "code-red": "^1.0.3",
+        "css-tree": "^2.3.1",
+        "estree-walker": "^3.0.3",
+        "is-reference": "^3.0.1",
+        "locate-character": "^3.0.0",
+        "magic-string": "^0.30.4",
+        "periscopic": "^3.1.0"
+      },
       "engines": {
-        "node": ">= 8"
+        "node": ">=16"
+      }
+    },
+    "node_modules/svelte/node_modules/estree-walker": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz",
+      "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==",
+      "dev": true,
+      "dependencies": {
+        "@types/estree": "^1.0.0"
       }
     },
     "node_modules/symlink-dir": {
diff --git a/package.json b/package.json
index ddd051b4..9a6216a8 100644
--- a/package.json
+++ b/package.json
@@ -259,7 +259,7 @@
     "npm-run-all": "4.1.5",
     "prettier": "3.3.3",
     "proxyquire": "2.1.3",
-    "svelte": "3.59.1",
+    "svelte": "4.2.18",
     "symlink-dir": "6.0.1",
     "typescript": "5.5.3",
     "upem": "9.0.4",
@@ -294,7 +294,7 @@
       {
         "package": "svelte",
         "policy": "pin",
-        "because": "upgrading to supporting svelte 4 will require a non-trivial amount of labour (which we are going to invest, but not right now)"
+        "because": "upgrading to supporting svelte 5 will require a non-trivial amount of labour (which we are going to invest, but not right now)"
       }
     ]
   },
@@ -306,7 +306,7 @@
     "coffee-script": ">=1.0.0 <2.0.0",
     "coffeescript": ">=1.0.0 <3.0.0",
     "livescript": ">=1.0.0 <2.0.0",
-    "svelte": ">=3.0.0 <4.0.0",
+    "svelte": ">=3.0.0 <5.0.0",
     "swc": ">=1.0.0 <2.0.0",
     "typescript": ">=2.0.0 <6.0.0",
     "vue-template-compiler": ">=2.0.0 <3.0.0",
diff --git a/src/meta.cjs b/src/meta.cjs
index 4adc596d..f72c8ccc 100644
--- a/src/meta.cjs
+++ b/src/meta.cjs
@@ -10,7 +10,7 @@ module.exports = {
     "coffee-script": ">=1.0.0 <2.0.0",
     coffeescript: ">=1.0.0 <3.0.0",
     livescript: ">=1.0.0 <2.0.0",
-    svelte: ">=3.0.0 <4.0.0",
+    svelte: ">=3.0.0 <5.0.0",
     swc: ">=1.0.0 <2.0.0",
     typescript: ">=2.0.0 <6.0.0",
     "vue-template-compiler": ">=2.0.0 <3.0.0",


Test patch:
diff --git a/test/extract/transpile/__fixtures__/svelte-too.js b/test/extract/transpile/__fixtures__/svelte-too.js
index bdf31c2c..1d47175c 100644
--- a/test/extract/transpile/__fixtures__/svelte-too.js
+++ b/test/extract/transpile/__fixtures__/svelte-too.js
@@ -1,4 +1,4 @@
-/* generated by Svelte v3.59.1 */
+/* generated by Svelte v4.2.18 */
 import {
   SvelteComponent,
   append,
@@ -15,6 +15,7 @@ import {
   transition_out,
 } from "svelte/internal";
 
+import "svelte/internal/disclose-version";
 import "./page.css";
 import Header from "./Header.svelte";
 
@@ -39,9 +40,7 @@ function create_fragment(ctx) {
       create_component(header.$$.fragment);
       t0 = space();
       section = element("section");
-
-      section.innerHTML = `<h2>Pages in Storybook</h2> 
-    <img src="./my.png" alt="my alt"/>`;
+      section.innerHTML = `<h2>Pages in Storybook</h2> <img src="./my.png" alt="my alt"/>`;
     },
     m(target, anchor) {
       insert(target, article, anchor);
@@ -66,7 +65,10 @@ function create_fragment(ctx) {
       current = false;
     },
     d(detaching) {
-      if (detaching) detach(article);
+      if (detaching) {
+        detach(article);
+      }
+
       destroy_component(header);
     },
   };
diff --git a/test/extract/transpile/__fixtures__/svelte.js b/test/extract/transpile/__fixtures__/svelte.js
index 95edad14..df04fee7 100644
--- a/test/extract/transpile/__fixtures__/svelte.js
+++ b/test/extract/transpile/__fixtures__/svelte.js
@@ -1,4 +1,4 @@
-/* generated by Svelte v3.59.1 */
+/* generated by Svelte v4.2.18 */
 import {
   SvelteComponent,
   append,
@@ -15,6 +15,7 @@ import {
   transition_out,
 } from "svelte/internal";
 
+import "svelte/internal/disclose-version";
 import "./page.css";
 
 function create_fragment(ctx) {
@@ -38,9 +39,7 @@ function create_fragment(ctx) {
       create_component(header.$$.fragment);
       t0 = space();
       section = element("section");
-
-      section.innerHTML = `<h2>Pages in Storybook</h2> 
-    <img src="./my.png" alt="my alt"/>`;
+      section.innerHTML = `<h2>Pages in Storybook</h2> <img src="./my.png" alt="my alt"/>`;
     },
     m(target, anchor) {
       insert(target, article, anchor);
@@ -65,7 +64,10 @@ function create_fragment(ctx) {
       current = false;
     },
     d(detaching) {
-      if (detaching) detach(article);
+      if (detaching) {
+        detach(article);
+      }
+
       destroy_component(header);
     },
   };
diff --git a/test/extract/transpile/meta.spec.mjs b/test/extract/transpile/meta.spec.mjs
index c4a8ab76..3fc56b3b 100644
--- a/test/extract/transpile/meta.spec.mjs
+++ b/test/extract/transpile/meta.spec.mjs
@@ -52,7 +52,7 @@ describe("[U] extract/transpile/meta", () => {
       },
       {
         name: "svelte",
-        version: ">=3.0.0 <4.0.0",
+        version: ">=3.0.0 <5.0.0",
         available: true,
       },
       {
diff --git a/test/extract/transpile/svelte-preprocess.spec.mjs b/test/extract/transpile/svelte-preprocess.spec.mjs
index 67745ba7..5513a953 100644
--- a/test/extract/transpile/svelte-preprocess.spec.mjs
+++ b/test/extract/transpile/svelte-preprocess.spec.mjs
@@ -1,5 +1,4 @@
 import { equal } from "node:assert/strict";
-// eslint-disable-next-line node/file-extension-in-import
 import * as svelteCompiler from "svelte/compiler";
 import normalizeNewline from "normalize-newline";
 import thing from "#extract/transpile/typescript-wrap.mjs";