-
-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit 95dbca0
Showing
16 changed files
with
238 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
on: | ||
push: | ||
branches-ignore: | ||
- "gh-pages" | ||
jobs: | ||
build: | ||
runs-on: ${{ matrix.os }} | ||
strategy: | ||
matrix: | ||
os: ["ubuntu-latest", "macos-latest", "windows-latest"] | ||
node: ["16", "18", "20"] | ||
name: Node.js ${{ matrix.node }} on ${{ matrix.os }} | ||
steps: | ||
- uses: actions/checkout@v3 | ||
- name: Setup node | ||
uses: actions/setup-node@v3 | ||
with: | ||
node-version: ${{ matrix.node }} | ||
- run: npm install | ||
- run: npm test | ||
env: | ||
YARN_GPG: no |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
node_modules | ||
package-lock.json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
MIT License | ||
|
||
Copyright (c) 2023 Zach Leatherman | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
# `@11ty/dependency-tree-esm` | ||
|
||
Returns an unordered array of local paths to dependencies of a Node ES module JavaScript file. | ||
|
||
This is used by Eleventy to find dependencies of a JavaScript file to watch for changes to re-run Eleventy’s build. | ||
|
||
## Installation | ||
|
||
``` | ||
npm install --save-dev @11ty/dependency-tree-esm | ||
``` | ||
|
||
## Features | ||
|
||
* Ignores `node_modules` | ||
* Ignores Node’s built-ins (e.g. `path`) | ||
* Handles circular dependencies | ||
* Returns an empty set if the file does not exist. | ||
|
||
## Usage | ||
|
||
```js | ||
// my-file.js | ||
|
||
// if my-local-dependency.js has dependencies, it will include those too | ||
const test = require("./my-local-dependency.js"); | ||
|
||
// ignored, is a built-in | ||
const path = require("path"); | ||
``` | ||
|
||
```js | ||
const DependencyTree = require("@11ty/dependency-tree"); | ||
|
||
DependencyTree("./my-file.js"); | ||
// returns ["./my-local-dependency.js"] | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
import * as acorn from "acorn"; | ||
import { readFile } from "fs/promises"; | ||
import { default as normalizePath } from "normalize-path"; | ||
import path from "path"; | ||
import { TemplatePath } from "@11ty/eleventy-utils"; | ||
|
||
// Is *not* a bare specifier (e.g. 'some-package') | ||
// https://nodejs.org/dist/latest-v18.x/docs/api/esm.html#terminology | ||
function isNonBareSpecifier(importSource) { | ||
// Change \\ to / on Windows | ||
let normalized = normalizePath(importSource); | ||
// Relative specifier (e.g. './startup.js') | ||
if(normalized.startsWith("./") || normalized.startsWith("../")) { | ||
return true; | ||
} | ||
// Absolute specifier (e.g. 'file:///opt/nodejs/config.js') | ||
if(normalized.startsWith("file:")) { | ||
return true; | ||
} | ||
|
||
return false; | ||
} | ||
|
||
function normalizeFilePath(filePath) { | ||
return TemplatePath.addLeadingDotSlash(path.relative(".", filePath)); | ||
} | ||
|
||
function normalizeImportSourceToFilePath(filePath, source) { | ||
let { dir } = path.parse(filePath); | ||
let normalized = path.join(dir, source); | ||
return TemplatePath.addLeadingDotSlash(path.relative(".", normalized)); | ||
} | ||
|
||
async function findByContents(contents, filePath, alreadyParsedSet) { | ||
// Should we use dependency-graph for these relationships? | ||
let sources = new Set(); | ||
|
||
let ast = acorn.parse(contents, {sourceType: "module", ecmaVersion: "latest"}); | ||
for(let node of ast.body) { | ||
if(node.type === "ImportDeclaration" && isNonBareSpecifier(node.source.value)) { | ||
let normalized = normalizeImportSourceToFilePath(filePath, node.source.value); | ||
if(sources.has(normalized) || normalized === filePath) { | ||
continue; | ||
} | ||
|
||
sources.add(normalized); | ||
} | ||
} | ||
|
||
// Recurse for nested deps | ||
for(let source of sources) { | ||
let s = await find(source, alreadyParsedSet); | ||
for(let p of s) { | ||
if(sources.has(p) || p === filePath) { | ||
continue; | ||
} | ||
|
||
sources.add(p); | ||
} | ||
} | ||
|
||
return Array.from(sources); | ||
} | ||
|
||
export async function find(filePath, alreadyParsedSet = new Set()) { | ||
// TODO add a cache here | ||
// Unfortunately we need to read the entire file, imports need to be at the top level but they can be anywhere 🫠 | ||
let normalized = normalizeFilePath(filePath); | ||
if(alreadyParsedSet.has(normalized)) { | ||
return []; | ||
} | ||
alreadyParsedSet.add(normalized); | ||
|
||
let contents = await readFile(normalized, { encoding: 'utf8' }); | ||
let sources = await findByContents(contents, normalized, alreadyParsedSet); | ||
|
||
return sources; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
{ | ||
"name": "@11ty/dependency-tree-esm", | ||
"version": "1.0.0", | ||
"description": "Finds all JavaScript ES Module dependencies from a filename.", | ||
"type": "module", | ||
"main": "main.js", | ||
"scripts": { | ||
"test": "npx ava" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/11ty/eleventy-dependency-tree.git" | ||
}, | ||
"author": { | ||
"name": "Zach Leatherman", | ||
"email": "[email protected]", | ||
"url": "https://zachleat.com/" | ||
}, | ||
"license": "MIT", | ||
"dependencies": { | ||
"@11ty/eleventy-utils": "^1.0.1", | ||
"acorn": "^8.10.0", | ||
"dependency-graph": "^0.11.0", | ||
"normalize-path": "^3.0.0" | ||
}, | ||
"devDependencies": { | ||
"ava": "^5.3.1" | ||
}, | ||
"ava": { | ||
"files": [ | ||
"./test/*.js" | ||
], | ||
"ignoredByWatcher": [ | ||
"./test/stubs/**" | ||
] | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
import "./circular-parent.js"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
import "./circular-child.js"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
import "./circular-self.js"; | ||
import "./empty.js"; |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
import fs from "fs"; | ||
import * as fdklsjf from "./imported-secondary.js"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import "fs"; | ||
|
||
export function hello() {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
import * as fdklsjf from "./imported-secondary.js"; | ||
|
||
export function hello() {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
import * as fdklsjf from "./nested.js"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
import fs from "fs"; | ||
import * as fdklsjf from "./imported.js"; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import test from "ava"; | ||
import { find } from "../main.js"; | ||
|
||
test("Empty", async t => { | ||
t.deepEqual(await find("./test/stubs/empty.js"), []); | ||
}); | ||
|
||
test("Simple", async t => { | ||
t.deepEqual(await find("./test/stubs/file.js"), ["./test/stubs/imported-secondary.js"]); | ||
}); | ||
|
||
test("Nested two deep", async t => { | ||
t.deepEqual(await find("./test/stubs/nested.js"), ["./test/stubs/imported.js", "./test/stubs/imported-secondary.js"]); | ||
}); | ||
|
||
test("Nested three deep", async t => { | ||
t.deepEqual(await find("./test/stubs/nested-grandchild.js"), ["./test/stubs/nested.js", "./test/stubs/imported.js", "./test/stubs/imported-secondary.js"]); | ||
}); | ||
|
||
test("Circular", async t => { | ||
t.deepEqual(await find("./test/stubs/circular-parent.js"), ["./test/stubs/circular-child.js"]); | ||
}); | ||
|
||
test("Circular Self Reference", async t => { | ||
t.deepEqual(await find("./test/stubs/circular-self.js"), ["./test/stubs/empty.js"]); | ||
}); |