mirror of
https://github.com/zhouhua/obsidian-waveform-player.git
synced 2026-07-22 05:41:57 +00:00
init
This commit is contained in:
parent
3b7e927bf0
commit
0d64c0d64f
13 changed files with 6888 additions and 0 deletions
15
.github/FUNDING.yml
vendored
Normal file
15
.github/FUNDING.yml
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
# These are supported funding model platforms
|
||||
|
||||
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
|
||||
patreon: # Replace with a single Patreon username
|
||||
open_collective: # Replace with a single Open Collective username
|
||||
ko_fi: zhouhua
|
||||
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
|
||||
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
|
||||
liberapay: # Replace with a single Liberapay username
|
||||
issuehunt: # Replace with a single IssueHunt username
|
||||
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
|
||||
polar: # Replace with a single Polar username
|
||||
buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
|
||||
thanks_dev: # Replace with a single thanks.dev username
|
||||
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
|
||||
40
.github/workflows/release.yml
vendored
Normal file
40
.github/workflows/release.yml
vendored
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
name: Release Obsidian plugin
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- "*"
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
submodules: recursive
|
||||
|
||||
- name: Use Node.js
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: "20.x"
|
||||
|
||||
- name: setup pnpm
|
||||
uses: pnpm/action-setup@v3
|
||||
with:
|
||||
version: latest
|
||||
run_install: true
|
||||
|
||||
- name: Build plugin
|
||||
run: pnpm build
|
||||
|
||||
- name: Create release
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
tag="${GITHUB_REF#refs/tags/}"
|
||||
|
||||
gh release create "$tag" \
|
||||
--title="$tag" \
|
||||
main.js manifest.json styles.css
|
||||
44
.gitignore
vendored
Normal file
44
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
# Dependencies
|
||||
node_modules
|
||||
.pnp
|
||||
.pnp.js
|
||||
|
||||
# Testing
|
||||
coverage
|
||||
|
||||
# Production
|
||||
build
|
||||
dist
|
||||
storybook-static
|
||||
|
||||
# Misc
|
||||
.DS_Store
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
.env
|
||||
|
||||
# Logs
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# Editor directories and files
|
||||
.idea
|
||||
.vscode
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
|
||||
# TypeScript
|
||||
*.tsbuildinfo
|
||||
|
||||
# Cache
|
||||
.cache
|
||||
.parcel-cache
|
||||
|
||||
main.js
|
||||
styles.css
|
||||
40
README.md
Normal file
40
README.md
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
# Obsidian Audio Beautifier
|
||||
|
||||
A plugin that enhances the display of audio files in Obsidian, supporting both audio attachments and recorded audio files.
|
||||
|
||||
## Features
|
||||
|
||||
- Beautiful audio player interface
|
||||
- Support for audio attachments (MP3, WAV, etc.)
|
||||
- Support for Obsidian recorded audio
|
||||
|
||||
## Preview
|
||||
|
||||
### Before
|
||||

|
||||
|
||||
### After
|
||||

|
||||
|
||||
## Installation
|
||||
|
||||
### From Obsidian Community Plugins
|
||||
1. Open Obsidian Settings
|
||||
2. Go to `Community Plugins`
|
||||
3. Disable `Safe Mode`
|
||||
4. Search for "Audio Beautifier"
|
||||
5. Click Install
|
||||
6. Enable the plugin
|
||||
|
||||
## Usage
|
||||
|
||||
Once installed, the plugin automatically enhances the appearance of all audio files in your vault. No additional configuration needed.
|
||||
|
||||
## My Other Obsidian Plugins
|
||||
|
||||
* [Export Image](https://github.com/zhouhua/obsidian-export-image)
|
||||
* [Markdown Media Card](https://github.com/zhouhua/obsidian-markdown-media-card)
|
||||
* [vConsole](https://github.com/zhouhua/obsidian-vconsole)
|
||||
* [POWER MODE](https://github.com/zhouhua/obsidian-power-mode)
|
||||
* [PopKit](https://github.com/zhouhua/obsidian-popkit)
|
||||
* [Another Sticky Headings](https://github.com/zhouhua/obsidian-sticky-headings)
|
||||
61
esbuild.config.mjs
Normal file
61
esbuild.config.mjs
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
import process from 'node:process';
|
||||
import builtins from 'builtin-modules';
|
||||
import esbuild from 'esbuild';
|
||||
|
||||
const banner
|
||||
= `/*
|
||||
THIS IS A GENERATED/BUNDLED FILE BY ESBUILD
|
||||
if you want to view the source, please visit the github repository of this plugin
|
||||
*/
|
||||
`;
|
||||
|
||||
const prod = process.argv[2] === 'production';
|
||||
|
||||
const context = await esbuild.context({
|
||||
banner: {
|
||||
js: banner,
|
||||
},
|
||||
bundle: true,
|
||||
entryNames: '[dir]/[name]',
|
||||
entryPoints: [
|
||||
{ in: 'src/main.ts', out: 'main' },
|
||||
{ in: 'main.css', out: 'styles' },
|
||||
],
|
||||
external: [
|
||||
'obsidian',
|
||||
'electron',
|
||||
'@codemirror/autocomplete',
|
||||
'@codemirror/collab',
|
||||
'@codemirror/commands',
|
||||
'@codemirror/language',
|
||||
'@codemirror/lint',
|
||||
'@codemirror/search',
|
||||
'@codemirror/state',
|
||||
'@codemirror/view',
|
||||
'@lezer/common',
|
||||
'@lezer/highlight',
|
||||
'@lezer/lr',
|
||||
...builtins,
|
||||
],
|
||||
format: 'cjs',
|
||||
loader: {
|
||||
'.css': 'css',
|
||||
},
|
||||
logLevel: 'info',
|
||||
// outfile: "main.js",
|
||||
minify: false,
|
||||
outdir: '.',
|
||||
outExtension: { '.css': '.css' },
|
||||
platform: 'browser',
|
||||
sourcemap: prod ? false : 'inline',
|
||||
target: 'es2018',
|
||||
treeShaking: true,
|
||||
});
|
||||
|
||||
if (prod) {
|
||||
await context.rebuild();
|
||||
process.exit(0);
|
||||
}
|
||||
else {
|
||||
await context.watch();
|
||||
}
|
||||
3
eslint.config.js
Normal file
3
eslint.config.js
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
import baseConfig from '@waveform/inf/eslint';
|
||||
|
||||
export default baseConfig;
|
||||
70
main.css
Normal file
70
main.css
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
@import '@waveform-audio/player/index.css';
|
||||
|
||||
/* 基础按钮样式重置 */
|
||||
.wa-player button {
|
||||
box-shadow: none;
|
||||
width: auto;
|
||||
height: auto;
|
||||
background-color: transparent;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* 隐藏默认音频元素 */
|
||||
.internal-embed audio {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 播放器容器样式 */
|
||||
.waveform-player-widget {
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
display: block;
|
||||
width: 100%;
|
||||
background: var(--background-secondary);
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
|
||||
.waveform-player-widget-container {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
display: block;
|
||||
transition: all 0.3s ease;
|
||||
margin: 0.5em 0;
|
||||
}
|
||||
|
||||
/* 实时预览模式样式 */
|
||||
.markdown-source-view.mod-cm6.is-live-preview .cm-content .waveform-player-widget-container {
|
||||
display: block !important;
|
||||
visibility: visible !important;
|
||||
position: relative !important;
|
||||
pointer-events: auto !important;
|
||||
user-select: auto !important;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* 实时预览模式下的嵌套样式 */
|
||||
.markdown-source-view.mod-cm6.is-live-preview .cm-line .waveform-player-widget-container {
|
||||
margin: 0.5em 0 !important;
|
||||
padding-left: 0.5em !important;
|
||||
border-left: 2px solid var(--interactive-accent);
|
||||
}
|
||||
|
||||
/* 确保播放器在实时预览模式下可以交互 */
|
||||
.markdown-source-view.mod-cm6.is-live-preview .waveform-player-widget {
|
||||
pointer-events: auto !important;
|
||||
user-select: auto !important;
|
||||
position: relative !important;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
/* 优化间距 */
|
||||
.waveform-player-widget-container + .waveform-player-widget-container {
|
||||
margin-top: 1em !important;
|
||||
}
|
||||
|
||||
/* 统一播放器样式 */
|
||||
.wa-obsidian-player {
|
||||
background: var(--background-secondary);
|
||||
border-radius: 8px;
|
||||
padding: 0.5em;
|
||||
}
|
||||
6225
pnpm-lock.yaml
Normal file
6225
pnpm-lock.yaml
Normal file
File diff suppressed because it is too large
Load diff
BIN
screenshots/after.png
Normal file
BIN
screenshots/after.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 58 KiB |
BIN
screenshots/before.png
Normal file
BIN
screenshots/before.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.2 KiB |
337
src/main.ts
Normal file
337
src/main.ts
Normal file
|
|
@ -0,0 +1,337 @@
|
|||
import type { EditorState, Extension } from '@codemirror/state';
|
||||
import type { DecorationSet, ViewUpdate } from '@codemirror/view';
|
||||
import type { ComponentType } from 'react';
|
||||
import { StateEffect, StateField } from '@codemirror/state';
|
||||
import { Decoration, EditorView, ViewPlugin, WidgetType } from '@codemirror/view';
|
||||
import WaveformPlayer from '@waveform-audio/player';
|
||||
import { Plugin, TFile } from 'obsidian';
|
||||
import { createElement } from 'react';
|
||||
import { createRoot } from 'react-dom/client';
|
||||
|
||||
const PLAYER_CLASS = 'waveform-player-widget';
|
||||
const AUDIO_LINK_PATTERN = '!\\[([^\\]]*)\\]\\(([^)]+\\.(?:mp3|wav|ogg|m4a|webm))\\)';
|
||||
|
||||
class AudioPlayerWidget extends WidgetType {
|
||||
private static counter = 0;
|
||||
private container: HTMLElement | null = null;
|
||||
private readonly id: string;
|
||||
private mounted = false;
|
||||
private playerDiv: HTMLElement | null = null;
|
||||
private root: null | ReturnType<typeof createRoot> = null;
|
||||
|
||||
constructor(
|
||||
private readonly src: string,
|
||||
private readonly title: string,
|
||||
private readonly plugin: WaveformPlayerPlugin,
|
||||
) {
|
||||
super();
|
||||
this.id = `audio-player-${AudioPlayerWidget.counter++}`;
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.unmount();
|
||||
this.container = null;
|
||||
this.playerDiv = null;
|
||||
}
|
||||
|
||||
eq(other: AudioPlayerWidget): boolean {
|
||||
return (
|
||||
other instanceof AudioPlayerWidget &&
|
||||
this.src === other.src &&
|
||||
this.title === other.title
|
||||
);
|
||||
}
|
||||
|
||||
toDOM() {
|
||||
if (this.container) {
|
||||
this.unmount();
|
||||
}
|
||||
|
||||
const container = document.createElement('div');
|
||||
container.className = `${PLAYER_CLASS}-container`;
|
||||
container.dataset.playerId = this.id;
|
||||
|
||||
const playerDiv = document.createElement('div');
|
||||
playerDiv.className = PLAYER_CLASS;
|
||||
container.appendChild(playerDiv);
|
||||
|
||||
this.container = container;
|
||||
this.playerDiv = playerDiv;
|
||||
|
||||
// 使用 requestIdleCallback 延迟挂载,优化性能
|
||||
if (window.requestIdleCallback) {
|
||||
window.requestIdleCallback(() => this.mount());
|
||||
}
|
||||
else {
|
||||
setTimeout(() => this.mount(), 0);
|
||||
}
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
private mount() {
|
||||
if (!this.playerDiv || !this.container || this.mounted) {
|
||||
return;
|
||||
}
|
||||
const audioFile = this.plugin.getAudioFile(this.src);
|
||||
if (!audioFile) {
|
||||
console.warn('[AudioPlayerWidget] Audio file not found:', this.src);
|
||||
return;
|
||||
}
|
||||
|
||||
const audioUrl = this.plugin.app.vault.getResourcePath(audioFile);
|
||||
console.log("audioUrl", audioUrl);
|
||||
const decodedUrl = decodeURIComponent(audioUrl);
|
||||
|
||||
try {
|
||||
this.root = createRoot(this.playerDiv);
|
||||
this.root.render(
|
||||
createElement(WaveformPlayer as ComponentType<any>, {
|
||||
className: 'wa-obsidian-player',
|
||||
key: this.id,
|
||||
samplePoints: 200,
|
||||
src: decodedUrl,
|
||||
styles: {
|
||||
controls: {
|
||||
paddingBottom: 0,
|
||||
width: '156px',
|
||||
},
|
||||
root: {
|
||||
padding: '0.5em',
|
||||
},
|
||||
title: {
|
||||
fontSize: '14px',
|
||||
margin: 0,
|
||||
},
|
||||
waveform: {
|
||||
height: '100px',
|
||||
},
|
||||
},
|
||||
title: this.title || audioFile.basename,
|
||||
type: 'mirror',
|
||||
}),
|
||||
);
|
||||
this.mounted = true;
|
||||
}
|
||||
catch (error) {
|
||||
console.error('[AudioPlayerWidget] Failed to mount player:', error);
|
||||
}
|
||||
}
|
||||
|
||||
private unmount() {
|
||||
if (this.root) {
|
||||
try {
|
||||
this.root.unmount();
|
||||
}
|
||||
catch (error) {
|
||||
console.error('[AudioPlayerWidget] Failed to unmount player:', error);
|
||||
}
|
||||
this.root = null;
|
||||
}
|
||||
if (this.playerDiv) {
|
||||
this.playerDiv.innerHTML = '';
|
||||
}
|
||||
this.mounted = false;
|
||||
}
|
||||
}
|
||||
|
||||
export default class WaveformPlayerPlugin extends Plugin {
|
||||
createEditorExtension(): Extension {
|
||||
const updateAudioPlayers = StateEffect.define<void>();
|
||||
|
||||
const audioPlayerField = StateField.define<DecorationSet>({
|
||||
create: (state) => {
|
||||
return this.buildDecorations(state);
|
||||
},
|
||||
provide: field => EditorView.decorations.from(field),
|
||||
update: (decorations, tr) => {
|
||||
if (!tr.docChanged && !tr.effects.some(e => e.is(updateAudioPlayers))) {
|
||||
return decorations;
|
||||
}
|
||||
|
||||
const changes = tr.changes;
|
||||
let needsUpdate = false;
|
||||
|
||||
if (tr.docChanged) {
|
||||
const regex = new RegExp(AUDIO_LINK_PATTERN);
|
||||
/* @ts-ignore */
|
||||
changes.iterChanges((fromA, toA, fromB, toB, inserted) => {
|
||||
const insertedText = inserted.toString();
|
||||
const hasAudioLink = regex.test(insertedText);
|
||||
if (hasAudioLink) {
|
||||
needsUpdate = true;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!needsUpdate && !tr.effects.some(e => e.is(updateAudioPlayers))) {
|
||||
return decorations;
|
||||
}
|
||||
|
||||
return this.buildDecorations(tr.state);
|
||||
},
|
||||
});
|
||||
|
||||
const viewportPlugin = ViewPlugin.fromClass(class {
|
||||
private updateScheduled = false;
|
||||
private timeout: NodeJS.Timeout | null = null;
|
||||
|
||||
constructor(private readonly view: EditorView) {
|
||||
this.scheduleUpdate();
|
||||
}
|
||||
|
||||
destroy() {
|
||||
if (this.timeout) {
|
||||
clearTimeout(this.timeout);
|
||||
}
|
||||
}
|
||||
|
||||
update(update: ViewUpdate) {
|
||||
if (update.viewportChanged) {
|
||||
this.scheduleUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
private scheduleUpdate() {
|
||||
if (this.updateScheduled) {
|
||||
return;
|
||||
}
|
||||
this.updateScheduled = true;
|
||||
|
||||
if (this.timeout) {
|
||||
clearTimeout(this.timeout);
|
||||
}
|
||||
|
||||
this.timeout = setTimeout(() => {
|
||||
this.updateScheduled = false;
|
||||
this.view.dispatch({
|
||||
effects: updateAudioPlayers.of(undefined),
|
||||
});
|
||||
}, 200);
|
||||
}
|
||||
});
|
||||
|
||||
return [audioPlayerField, viewportPlugin];
|
||||
}
|
||||
|
||||
getAudioFile(src: string): null | TFile {
|
||||
const decodedSrc = decodeURIComponent(src);
|
||||
let audioFile = this.app.vault.getAbstractFileByPath(decodedSrc);
|
||||
if (!audioFile || !(audioFile instanceof TFile)) {
|
||||
const attachPath = this.app.vault.config.attachmentFolderPath;
|
||||
if (!attachPath) {
|
||||
return null;
|
||||
}
|
||||
|
||||
audioFile = this.app.vault.getAbstractFileByPath(`${attachPath}/${decodedSrc}`);
|
||||
if (!audioFile || !(audioFile instanceof TFile)) {
|
||||
audioFile = this.app.vault.getAbstractFileByPath(`${attachPath}/${src}`);
|
||||
if (!audioFile || !(audioFile instanceof TFile)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return audioFile instanceof TFile ? audioFile : null;
|
||||
}
|
||||
|
||||
async onload() {
|
||||
// 注册编辑器扩展
|
||||
this.registerEditorExtension(this.createEditorExtension());
|
||||
|
||||
// 注册 Markdown 后处理器(用于阅读视图)
|
||||
this.registerMarkdownPostProcessor((element) => {
|
||||
const audioElements = element.querySelectorAll('.internal-embed');
|
||||
|
||||
audioElements.forEach((div) => {
|
||||
const src = div.getAttribute('src');
|
||||
if (!src || !/\.(?:mp3|wav|ogg|m4a|webm)$/i.test(src)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const audioFile = this.getAudioFile(src);
|
||||
if (!audioFile) {
|
||||
return;
|
||||
}
|
||||
|
||||
const audioUrl = this.app.vault.getResourcePath(audioFile);
|
||||
const decodedUrl = decodeURIComponent(audioUrl);
|
||||
|
||||
const container = document.createElement('div');
|
||||
container.className = `${PLAYER_CLASS}-container`;
|
||||
|
||||
const playerDiv = document.createElement('div');
|
||||
playerDiv.className = PLAYER_CLASS;
|
||||
container.appendChild(playerDiv);
|
||||
|
||||
// 在原有元素后面插入播放器
|
||||
div.parentNode?.insertBefore(container, div.nextSibling);
|
||||
|
||||
const root = createRoot(playerDiv);
|
||||
root.render(
|
||||
createElement(WaveformPlayer as ComponentType<any>, {
|
||||
className: 'wa-obsidian-player',
|
||||
samplePoints: 200,
|
||||
src: decodedUrl,
|
||||
styles: {
|
||||
controls: {
|
||||
paddingBottom: 0,
|
||||
width: '156px',
|
||||
},
|
||||
root: {
|
||||
padding: '0.5em',
|
||||
},
|
||||
title: {
|
||||
fontSize: '14px',
|
||||
margin: 0,
|
||||
},
|
||||
waveform: {
|
||||
height: '100px',
|
||||
},
|
||||
},
|
||||
type: 'mirror',
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
onunload() {
|
||||
}
|
||||
|
||||
private buildDecorations(state: EditorState): DecorationSet {
|
||||
const widgets: any[] = [];
|
||||
const doc = state.doc;
|
||||
|
||||
// 遍历所有行,注意 CodeMirror 6 中行号从 1 开始
|
||||
for (let lineNo = 1; lineNo <= doc.lines; lineNo++) {
|
||||
const line = doc.line(lineNo);
|
||||
const lineText = line.text;
|
||||
|
||||
// 每次创建新的正则表达式实例
|
||||
const regex = new RegExp(AUDIO_LINK_PATTERN, 'gi');
|
||||
|
||||
// 使用 matchAll 替代 while 循环
|
||||
Array.from(lineText.matchAll(regex)).forEach((match) => {
|
||||
const [, title, src] = match;
|
||||
if (!src) {
|
||||
return;
|
||||
}
|
||||
|
||||
const matchStart = line.from + match.index!;
|
||||
const matchEnd = matchStart + match[0].length;
|
||||
|
||||
// 添加播放器装饰器,直接在链接后面插入
|
||||
widgets.push(
|
||||
Decoration.widget({
|
||||
block: true, // 添加 block 属性
|
||||
persistent: true,
|
||||
side: 1, // 在匹配文本后面插入
|
||||
widget: new AudioPlayerWidget(src, title || '', this),
|
||||
}).range(matchEnd),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
return Decoration.set(widgets, true);
|
||||
}
|
||||
}
|
||||
35
tsconfig.json
Normal file
35
tsconfig.json
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"extends": "@waveform-audio/inf/tsconfig",
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"inlineSourceMap": true,
|
||||
"inlineSources": true,
|
||||
"module": "ESNext",
|
||||
"target": "ES6",
|
||||
"allowJs": true,
|
||||
"noImplicitAny": true,
|
||||
"moduleResolution": "node",
|
||||
"importHelpers": true,
|
||||
"isolatedModules": true,
|
||||
"strictNullChecks": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"lib": [
|
||||
"DOM",
|
||||
"ES5",
|
||||
"ES6",
|
||||
"ES7",
|
||||
"ES2021"
|
||||
],
|
||||
"jsx": "react-jsx",
|
||||
"jsxImportSource": "react",
|
||||
"types": [
|
||||
"obsidian-typings"
|
||||
]
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts",
|
||||
"src/**/*.tsx"
|
||||
]
|
||||
}
|
||||
18
version-bump.mjs
Normal file
18
version-bump.mjs
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
|
||||
|
||||
import { readFileSync, writeFileSync } from 'fs';
|
||||
import process from 'process';
|
||||
|
||||
const targetVersion = process.env.npm_package_version;
|
||||
|
||||
// read minAppVersion from manifest.json and bump version to target version
|
||||
let manifest = JSON.parse(readFileSync('manifest.json', 'utf8'));
|
||||
const { minAppVersion } = manifest;
|
||||
manifest.version = targetVersion;
|
||||
writeFileSync('manifest.json', JSON.stringify(manifest, null, '\t'));
|
||||
|
||||
// update versions.json with target version and minAppVersion from manifest.json
|
||||
let versions = JSON.parse(readFileSync('versions.json', 'utf8'));
|
||||
versions[targetVersion] = minAppVersion;
|
||||
writeFileSync('versions.json', JSON.stringify(versions, null, '\t'));
|
||||
Loading…
Reference in a new issue