Skip to content

Commit f7ef29a

Browse files
committed
feat(project-files): project files tool (#39)
1 parent e772c63 commit f7ef29a

15 files changed

Lines changed: 1116 additions & 452 deletions

File tree

.npmrc

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
strict-peer-dependencies=false

README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,20 @@ Scripts and configs for TrigenSoftware's projects.
2525
|---------|---------|--------------|
2626
| [`@trigen/scripts`](packages/scripts#readme) | [![NPM version][npm]][npm-url] | [![Dependencies status][deps]][deps-url] |
2727
| [`@trigen/babel-preset`](packages/babel-preset#readme) | [![NPM version][babel-preset-npm]][babel-preset-npm-url] | [![Dependencies status][babel-preset-deps]][babel-preset-deps-url] |
28+
| [`@trigen/project-files`](packages/project-files#readme) | [![NPM version][project-files-npm]][project-files-npm-url] | [![Dependencies status][project-files-deps]][project-files-deps-url] |
2829
| [`@trigen/browserslist-config`](packages/browserslist-config#readme) | [![NPM version][browserslist-config-npm]][browserslist-config-npm-url] | |
2930
| [`@trigen/eslint-config`](packages/eslint-config#readme) | [![NPM version][eslint-config-npm]][eslint-config-npm-url] | [![Dependencies status][eslint-config-deps]][eslint-config-deps-url] |
3031
| [`@trigen/lint-package-json`](packages/lint-package-json#readme) | [![NPM version][lint-package-json-npm]][lint-package-json-npm-url] | [![Dependencies status][lint-package-json-deps]][lint-package-json-deps-url] |
3132
| [`@trigen/npm-package-json-lint-config`](packages/npm-package-json-lint-config#readme) | [![NPM version][npm-package-json-lint-config-npm]][npm-package-json-lint-config-npm-url] | [![Dependencies status][npm-package-json-lint-config-deps]][npm-package-json-lint-config-deps-url] |
3233

34+
<!-- project-files -->
35+
36+
[project-files-npm]: https://img.shields.io/npm/v/%40trigen/project-files.svg
37+
[project-files-npm-url]: https://www.npmjs.com/package/@trigen/project-files
38+
39+
[project-files-deps]: https://img.shields.io/librariesio/release/npm/@trigen/project-files
40+
[project-files-deps-url]: https://libraries.io/npm/@trigen%2Fproject-files/tree
41+
3342
<!-- babel-preset -->
3443

3544
[babel-preset-npm]: https://img.shields.io/npm/v/%40trigen/babel-preset.svg

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@
3838
"@trigen/browserslist-config": "workspace:*",
3939
"@trigen/eslint-config": "workspace:*",
4040
"@trigen/lint-package-json": "workspace:*",
41+
"@trigen/project-files": "workspace:*",
4142
"@trigen/scripts": "workspace:*",
4243
"@types/node": "^17.0.8",
4344
"clean-publish": "^4.0.0",
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"extends": [
3+
"@trigen/eslint-config/esm",
4+
"@trigen/eslint-config/jest"
5+
]
6+
}

packages/project-files/files.txt

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# GitHub
2+
.github/ISSUE_TEMPLATE/config.yml
3+
.github/ISSUE_TEMPLATE/bug-report.yml
4+
.github/ISSUE_TEMPLATE/feature-request.yml
5+
.github/ISSUE_TEMPLATE/question.yml
6+
# GitHub Actions
7+
.github/workflows/ci.yml
8+
.github/workflows/commit.yml
9+
.github/workflows/checks.yml
10+
.github/workflows/release.yml
11+
.github/workflows/update-snapshots.yml
12+
.github/workflows/website.yml
13+
# CI
14+
.github/renovate.json
15+
.clean-publish
16+
.eslintrc.json
17+
test/.eslintrc.json
18+
.size-limit.json
19+
.npmpackagejsonlintrc.json
20+
jest.config.json
21+
# TypeScript
22+
test/tsconfig.json
23+
tsconfig.json
24+
# Build
25+
env.d.ts
26+
.browserslistrc
27+
rollup.config.js
28+
# Monorepo
29+
lerna.json
30+
pnpm-workspace.yaml
31+
# Git Flow
32+
.gitignore
33+
.nano-staged.json
34+
.simple-git-hooks.json
35+
.commitlintrc.json
36+
.czrc
37+
# Basics
38+
.npmrc
39+
.editorconfig
40+
LICENSE
41+
README.md
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
{
2+
"name": "@trigen/project-files",
3+
"type": "module",
4+
"version": "8.0.0-alpha.9",
5+
"description": "CLI tool for download snippets files.",
6+
"author": "dangreen",
7+
"license": "MIT",
8+
"repository": {
9+
"type": "git",
10+
"url": "https://github.com/TrigenSoftware/scripts.git",
11+
"directory": "packages/project-files"
12+
},
13+
"bugs": {
14+
"url": "https://github.com/TrigenSoftware/scripts/issues"
15+
},
16+
"keywords": [
17+
"files",
18+
"snippets"
19+
],
20+
"engines": {
21+
"node": ">=14"
22+
},
23+
"bin": "./src/bin.js",
24+
"exports": "./src/index.js",
25+
"publishConfig": {
26+
"access": "public",
27+
"directory": "package"
28+
},
29+
"scripts": {
30+
"prepublishOnly": "del ./package && clean-publish",
31+
"postpublish": "del ./package"
32+
},
33+
"dependencies": {
34+
"@octokit/rest": "^19.0.4",
35+
"inquirer": "^8.2.0"
36+
},
37+
"readme": ""
38+
}

packages/project-files/sources.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
TrigenSoftware/scripts
2+
TrigenSoftware/simple-github-release
3+
TrigenSoftware/Argue
4+
canvg/canvg
5+
reactchartjs/react-chartjs-2
6+
chartist-js/chartist
7+
TrigenSoftware/flexis-srcset

packages/project-files/src/api.js

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
import { dirname, join } from 'path'
2+
import { Octokit } from '@octokit/rest'
3+
import { writeFile } from './fs.js'
4+
5+
/**
6+
* @typedef {import('./parse.js').SourceItem} Repo
7+
*/
8+
9+
const OK = 200
10+
const { GITHUB_TOKEN } = process.env
11+
const client = new Octokit({
12+
auth: GITHUB_TOKEN ? `token ${GITHUB_TOKEN}` : ''
13+
})
14+
15+
/**
16+
* @typedef RepoFile
17+
* @property {string} path - File path.
18+
* @property {string} name - File name.
19+
* @property {string} sha - The SHA of the file.
20+
* @property {string} download_url - Download url.
21+
* @property {string} html_url - Html url.
22+
*/
23+
24+
/**
25+
* Get files list from repository.
26+
* @param {Repo} repo
27+
* @param {string} path
28+
* @returns {Promise<RepoFile[]>} File from the repository.
29+
*/
30+
export async function getDirectory(repo, path) {
31+
let status
32+
let data
33+
34+
try {
35+
({
36+
status,
37+
data
38+
} = await client.request('GET /repos/{owner}/{repo}/contents/{path}', {
39+
owner: repo.owner,
40+
repo: repo.repo,
41+
path: path === '.' ? '' : path
42+
}))
43+
} catch (err) {
44+
return []
45+
}
46+
47+
if (status !== OK || !data) {
48+
return []
49+
}
50+
51+
return data
52+
}
53+
54+
/**
55+
* Get files filtered by sha from repository.
56+
* @param {Repo[]} repos
57+
* @param {string[]} paths
58+
* @returns {Promise<Map<string, RepoFile[]>>} Files from the repository.
59+
*/
60+
export async function getFiles(repos, paths) {
61+
const dirs = new Set()
62+
const tasks = []
63+
const shas = []
64+
const filesMap = new Map()
65+
let task
66+
let dir
67+
let file
68+
let sha
69+
let files
70+
71+
paths.forEach((path) => {
72+
dirs.add(dirname(path))
73+
})
74+
75+
repos.forEach((repo) => {
76+
dirs.forEach((dir) => {
77+
tasks.push(getDirectory(repo, dir))
78+
})
79+
})
80+
81+
for (task of tasks) {
82+
dir = await task
83+
84+
for (file of dir) {
85+
if (paths.includes(file.path)) {
86+
sha = file.sha + file.path
87+
88+
if (!shas.includes(sha)) {
89+
shas.push(sha)
90+
91+
files = filesMap.get(file.path)
92+
93+
if (!files) {
94+
files = []
95+
filesMap.set(file.path, files)
96+
}
97+
98+
files.push(file)
99+
}
100+
}
101+
}
102+
}
103+
104+
return filesMap
105+
}
106+
107+
/**
108+
* Download file.
109+
* @param {RepoFile} file
110+
* @param {boolean | string} write
111+
* @returns {Promise<string>} File contents.
112+
*/
113+
export async function downloadFile(file, write) {
114+
const { data } = await client.request(`GET ${file.download_url}`)
115+
116+
if (write) {
117+
await writeFile(
118+
join(
119+
write === true
120+
? '.'
121+
: write,
122+
file.path
123+
),
124+
data
125+
)
126+
}
127+
128+
return data
129+
}

packages/project-files/src/bin.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#!/usr/bin/env node
2+
import { fileURLToPath } from 'url'
3+
import { dirname, join } from 'path'
4+
import {
5+
readFile,
6+
writeFile,
7+
parseFilesList,
8+
parseSourcesList,
9+
getFiles,
10+
downloadFile,
11+
askFiles,
12+
askSource
13+
} from './index.js'
14+
15+
const { PROJECT_FILES_DIR } = process.env
16+
const cwd = PROJECT_FILES_DIR ? join(process.cwd(), PROJECT_FILES_DIR) : process.cwd()
17+
const pkgRoot = join(dirname(fileURLToPath(import.meta.url)), '..')
18+
const parseFilesListTask = parseFilesList(readFile(join(pkgRoot, 'files.txt'), 'utf8'))
19+
const parseSourcesListTask = parseSourcesList(readFile(join(pkgRoot, 'sources.txt'), 'utf8'))
20+
const [filesList, sourcesList] = await Promise.all([parseFilesListTask, parseSourcesListTask])
21+
const selectedFiles = await askFiles(filesList)
22+
const files = await getFiles(sourcesList, selectedFiles)
23+
let file
24+
let fileSources
25+
const tasks = []
26+
27+
for (file of selectedFiles) {
28+
fileSources = files.get(file)
29+
30+
if (!fileSources) {
31+
tasks.push(writeFile(join(cwd, file), ''))
32+
} else if (fileSources.length === 1) {
33+
tasks.push(downloadFile(fileSources[0], cwd))
34+
} else {
35+
tasks.push(downloadFile(await askSource(fileSources), cwd))
36+
}
37+
}
38+
39+
await Promise.all(tasks)

packages/project-files/src/fs.js

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { dirname } from 'path'
2+
import { promises } from 'fs'
3+
4+
const { readFile, writeFile: wf, mkdir } = promises
5+
6+
export { readFile }
7+
8+
/**
9+
* Write file.
10+
* @param {string} path
11+
* @param {string} contents
12+
* @returns {Promise<void>}
13+
*/
14+
export async function writeFile(path, contents) {
15+
const dir = dirname(path)
16+
17+
try {
18+
await mkdir(dir, {
19+
recursive: true
20+
})
21+
} catch (err) {
22+
/* Ignore error */
23+
}
24+
25+
return wf(path, contents)
26+
}

0 commit comments

Comments
 (0)