Skip to content

Commit 3cca30d

Browse files
committed
feat: support inject .js file
1 parent d327e5a commit 3cca30d

File tree

4 files changed

+96
-6
lines changed

4 files changed

+96
-6
lines changed

package.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,15 @@
3939
"cheerio": "^1.0.0-rc.12",
4040
"clipanion": "^3.2.1",
4141
"consola": "^3.2.3",
42+
"csp-dev": "^1.0.2",
4243
"debug": "^4.3.5",
4344
"dl-vampire": "^2.1.1",
4445
"env-paths": "3",
4546
"esm-utils": "^4.3.0",
4647
"fs-extra": "^11.2.0",
4748
"inquirer": "^9.3.3",
4849
"less": "^4.2.0",
50+
"log-symbols": "^6.0.0",
4951
"promise.map": "^0.5.0",
5052
"proxy-agent": "^6.4.0",
5153
"sass": "^1.77.6",
@@ -56,6 +58,7 @@
5658
"@magicdawn/prettier-config": "^0.0.4",
5759
"@swc/core": "^1.6.7",
5860
"@swc/helpers": "^0.5.11",
61+
"@types/csp-dev": "^1.0.3",
5962
"@types/debug": "^4.1.12",
6063
"@types/fs-extra": "^11.0.4",
6164
"@types/inquirer": "^9.0.7",

pnpm-lock.yaml

Lines changed: 34 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/commands/common.ts

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import cheerio, { CheerioAPI } from 'cheerio'
1+
import { CheerioAPI, load as cheerioLoad } from 'cheerio'
22
import consola from 'consola'
3+
import ContentSecurityPolicy, { type DirectiveDescriptor } from 'csp-dev'
34
import debugFactory from 'debug'
45
import fse from 'fs-extra'
56
import path from 'path'
@@ -24,7 +25,7 @@ export async function prepare() {
2425
}
2526

2627
const htmlContent = fse.readFileSync(HTML_FILE, 'utf-8')
27-
const $ = cheerio.load(htmlContent, { decodeEntities: false })
28+
const $ = cheerioLoad(htmlContent, { decodeEntities: false })
2829
return $
2930
}
3031

@@ -40,6 +41,33 @@ export function save($: CheerioAPI) {
4041
export async function applyData() {
4142
const $ = await prepare()
4243

44+
// <meta http-equiv="Content-Security-Policy" content="
45+
{
46+
const cspMeta = $(`meta[http-equiv="Content-Security-Policy"]`)
47+
const cspContent = cspMeta.attr('content')
48+
49+
// csp-dev 没有 trim, 包含很多 `\t\t`
50+
const fixJson = (json: DirectiveDescriptor) => {
51+
Object.keys(json).forEach((key) => {
52+
const value = json[key] as string[]
53+
json[key] = value.map((x) => x.trim())
54+
})
55+
return json
56+
}
57+
58+
debugger
59+
const cspModel = new ContentSecurityPolicy(cspContent)
60+
const parsed = fixJson(cspModel.share('json'))
61+
62+
if (!parsed['script-src']?.includes(`'unsafe-inline'`)) {
63+
parsed['script-src'] = [...(parsed['script-src'] || []), `'unsafe-inline'`]
64+
const newModel = new ContentSecurityPolicy()
65+
newModel.load(parsed)
66+
const cspContentNew = newModel.share('string')
67+
cspMeta.attr('content', cspContentNew)
68+
}
69+
}
70+
4371
// remove all existing tags
4472
$(`[${DATA_ATTR_NAME}]`).remove()
4573

@@ -50,17 +78,17 @@ export async function applyData() {
5078
const content = await getContent(file)
5179
return { file, content }
5280
},
53-
5
81+
5,
5482
)
5583

5684
debug(
5785
'after filter out disabled: %O',
58-
listData.map((x) => x.file)
86+
listData.map((x) => x.file),
5987
)
6088

6189
// create new tags
6290
for (let { file, content } of listData) {
63-
const ext = path.extname(file)
91+
const ext = path.extname(file).slice(1)
6492
const tagName = ext === 'js' ? 'script' : 'style'
6593
const tag = `\n<${tagName} ${DATA_ATTR_NAME}='${file}'>\n${content}\n</${tagName}>\n`
6694
$('html').append(tag)

src/commands/list.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Command, Usage } from 'clipanion'
1+
import { Command, Option, Usage } from 'clipanion'
22
import inquirer from 'inquirer'
33
import { AddedAsset, CURRENT_ASSETS, write } from '../data'
44
import { applyData } from './common'
@@ -11,7 +11,19 @@ export class ListCommand extends Command {
1111
description: 'manage added files',
1212
}
1313

14+
interactive = Option.Boolean('-i,--interactive', false, {
15+
description: 'list & slect with an interactive prompt',
16+
})
17+
1418
async execute() {
19+
if (this.interactive) {
20+
return this.interactiveSelect()
21+
} else {
22+
return this.normalList()
23+
}
24+
}
25+
26+
interactiveSelect = async () => {
1527
const { selectedIndex } = await inquirer.prompt([
1628
{
1729
type: 'checkbox',
@@ -30,4 +42,17 @@ export class ListCommand extends Command {
3042

3143
await applyData()
3244
}
45+
46+
normalList = async () => {
47+
console.log('')
48+
console.log('Current added files: (✅ enabled, 🟩 disabled)')
49+
console.log('')
50+
51+
const msgs = CURRENT_ASSETS.map((item) => {
52+
// const symbol = item.disabled ? logSymbols.success : logSymbols.error
53+
const symbol = item.disabled ? '✅' : '🟩'
54+
return `${symbol} ${item.file}`
55+
})
56+
console.log(msgs.join('\n'))
57+
}
3358
}

0 commit comments

Comments
 (0)