Compare commits

..

11 Commits

Author SHA1 Message Date
github-actions[bot]
e24060c31d chore(main): release 15.1.1 (#1033)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-09-13 15:02:21 -03:00
absidue
a2c3774e9a fix(Player): Store the full library version in cache entries (#1032) 2025-09-12 16:10:19 -03:00
github-actions[bot]
dee2b07cb0 chore(main): release 15.1.0 (#1030)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-09-11 19:24:37 -03:00
dependabot[bot]
da0551cb4c chore(deps-dev): bump vite from 6.3.5 to 7.1.5 (#1028)
Bumps [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite) from 6.3.5 to 7.1.5.
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/main/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v7.1.5/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 7.1.5
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-09-11 19:23:58 -03:00
absidue
68a6af9b2c feat(parser): Add ListView, ListItemView and SubscribeButtonView (#1025) 2025-09-11 15:51:57 -03:00
absidue
46c2f6c6c1 refactor: Import version, bugs URL and repo URL directly from package.json (#1004) 2025-09-11 15:49:48 -03:00
absidue
95976de115 perf: Replace uses of ObservableArray#get with Array#find (#1013) 2025-09-11 15:45:16 -03:00
absidue
8be677adec fix(types): Parser.parseArray always returns an ObservedArray (#1014) 2025-09-11 15:43:58 -03:00
absidue
aa7cf561a7 feat(parser): Parse badges in ContentMetadataView (#1017) 2025-09-11 15:43:37 -03:00
absidue
bac896501b fix(parser): LockupMetadataView.image can also be an AvatarStackView (#1026) 2025-09-11 15:43:05 -03:00
absidue
3ea2815aba fix(Player): Fix global variable extraction in the deciphering code (#1029) 2025-09-11 15:42:27 -03:00
50 changed files with 276 additions and 159 deletions

View File

@@ -1,5 +1,32 @@
# Changelog
## [15.1.1](https://github.com/LuanRT/YouTube.js/compare/v15.1.0...v15.1.1) (2025-09-12)
### Bug Fixes
* **Player:** Store the full library version in cache entries ([#1032](https://github.com/LuanRT/YouTube.js/issues/1032)) ([a2c3774](https://github.com/LuanRT/YouTube.js/commit/a2c3774e9a0212d7aab7af2baee1f48ad3319a08))
## [15.1.0](https://github.com/LuanRT/YouTube.js/compare/v15.0.1...v15.1.0) (2025-09-11)
### Features
* **parser:** Add ListView, ListItemView and SubscribeButtonView ([#1025](https://github.com/LuanRT/YouTube.js/issues/1025)) ([68a6af9](https://github.com/LuanRT/YouTube.js/commit/68a6af9b2c2e4e5a31b2d6f5c5add6c238e5113e))
* **parser:** Parse badges in ContentMetadataView ([#1017](https://github.com/LuanRT/YouTube.js/issues/1017)) ([aa7cf56](https://github.com/LuanRT/YouTube.js/commit/aa7cf561a7cdec017383b9daa6c9401f08995d4c))
### Bug Fixes
* **parser:** LockupMetadataView.image can also be an AvatarStackView ([#1026](https://github.com/LuanRT/YouTube.js/issues/1026)) ([bac8965](https://github.com/LuanRT/YouTube.js/commit/bac896501b9525c28b319301151a0dde93d08ec0))
* **Player:** Fix global variable extraction in the deciphering code ([#1029](https://github.com/LuanRT/YouTube.js/issues/1029)) ([3ea2815](https://github.com/LuanRT/YouTube.js/commit/3ea2815abac03ae7371e45ae2f2758caf9db2266))
* **types:** Parser.parseArray always returns an ObservedArray ([#1014](https://github.com/LuanRT/YouTube.js/issues/1014)) ([8be677a](https://github.com/LuanRT/YouTube.js/commit/8be677adec6631be557c95adc8f687e5d01b4fdf))
### Performance Improvements
* Replace uses of ObservableArray#get with Array#find ([#1013](https://github.com/LuanRT/YouTube.js/issues/1013)) ([95976de](https://github.com/LuanRT/YouTube.js/commit/95976de115587d8f266bc44355440835f3b2b02f))
## [15.0.1](https://github.com/LuanRT/YouTube.js/compare/v15.0.0...v15.0.1) (2025-07-22)

75
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "youtubei.js",
"version": "15.0.1",
"version": "15.1.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "youtubei.js",
"version": "15.0.1",
"version": "15.1.1",
"funding": [
"https://github.com/sponsors/LuanRT"
],
@@ -30,7 +30,6 @@
"rimraf": "^6.0.1",
"ts-patch": "^3.0.2",
"ts-proto": "^2.2.0",
"ts-transformer-inline-file": "^0.2.0",
"typedoc": "^0.26.7",
"typedoc-plugin-markdown": "^4.2.7",
"typescript": "^5.0.0",
@@ -4731,14 +4730,14 @@
"license": "MIT"
},
"node_modules/tinyglobby": {
"version": "0.2.14",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz",
"integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==",
"version": "0.2.15",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
"integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"fdir": "^6.4.4",
"picomatch": "^4.0.2"
"fdir": "^6.5.0",
"picomatch": "^4.0.3"
},
"engines": {
"node": ">=12.0.0"
@@ -4748,11 +4747,14 @@
}
},
"node_modules/tinyglobby/node_modules/fdir": {
"version": "6.4.6",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz",
"integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==",
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12.0.0"
},
"peerDependencies": {
"picomatch": "^3 || ^4"
},
@@ -4919,18 +4921,6 @@
"@bufbuild/protobuf": "^2.0.0"
}
},
"node_modules/ts-transformer-inline-file": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/ts-transformer-inline-file/-/ts-transformer-inline-file-0.2.0.tgz",
"integrity": "sha512-16IBBt0d8MxVdEyxDEFx2+EetgELWs/kUu4FEaL4b1dffu5gB/aw+CwVPW3EzsuO67g8OAF34LcDHsyxQp16Aw==",
"dev": true,
"engines": {
"node": ">=14"
},
"peerDependencies": {
"typescript": "^4.0.0"
}
},
"node_modules/type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
@@ -5175,24 +5165,24 @@
}
},
"node_modules/vite": {
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz",
"integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==",
"version": "7.1.5",
"resolved": "https://registry.npmjs.org/vite/-/vite-7.1.5.tgz",
"integrity": "sha512-4cKBO9wR75r0BeIWWWId9XK9Lj6La5X846Zw9dFfzMRw38IlTk2iCcUt6hsyiDRcPidc55ZParFYDXi0nXOeLQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.4.4",
"picomatch": "^4.0.2",
"postcss": "^8.5.3",
"rollup": "^4.34.9",
"tinyglobby": "^0.2.13"
"fdir": "^6.5.0",
"picomatch": "^4.0.3",
"postcss": "^8.5.6",
"rollup": "^4.43.0",
"tinyglobby": "^0.2.15"
},
"bin": {
"vite": "bin/vite.js"
},
"engines": {
"node": "^18.0.0 || ^20.0.0 || >=22.0.0"
"node": "^20.19.0 || >=22.12.0"
},
"funding": {
"url": "https://github.com/vitejs/vite?sponsor=1"
@@ -5201,14 +5191,14 @@
"fsevents": "~2.3.3"
},
"peerDependencies": {
"@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
"@types/node": "^20.19.0 || >=22.12.0",
"jiti": ">=1.21.0",
"less": "*",
"less": "^4.0.0",
"lightningcss": "^1.21.0",
"sass": "*",
"sass-embedded": "*",
"stylus": "*",
"sugarss": "*",
"sass": "^1.70.0",
"sass-embedded": "^1.70.0",
"stylus": ">=0.54.8",
"sugarss": "^5.0.0",
"terser": "^5.16.0",
"tsx": "^4.8.1",
"yaml": "^2.4.2"
@@ -5273,11 +5263,14 @@
}
},
"node_modules/vite/node_modules/fdir": {
"version": "6.4.6",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz",
"integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==",
"version": "6.5.0",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
"integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
"dev": true,
"license": "MIT",
"engines": {
"node": ">=12.0.0"
},
"peerDependencies": {
"picomatch": "^3 || ^4"
},

View File

@@ -1,6 +1,6 @@
{
"name": "youtubei.js",
"version": "15.0.1",
"version": "15.1.1",
"description": "A JavaScript client for YouTube's private API, known as InnerTube.",
"type": "module",
"types": "./dist/src/platform/lib.d.ts",
@@ -38,6 +38,7 @@
"react-native": "./dist/src/platform/react-native.js",
"default": "./dist/src/platform/web.js"
},
"./package.json": "./package.json",
"./agnostic": {
"types": "./dist/src/platform/lib.d.ts",
"default": "./dist/src/platform/lib.js"
@@ -122,7 +123,6 @@
"rimraf": "^6.0.1",
"ts-patch": "^3.0.2",
"ts-proto": "^2.2.0",
"ts-transformer-inline-file": "^0.2.0",
"typedoc": "^0.26.7",
"typedoc-plugin-markdown": "^4.2.7",
"typescript": "^5.0.0",

View File

@@ -10,6 +10,7 @@ import {
Platform,
PlayerError
} from '../utils/Utils.js';
import packageInfo from '../../package.json' with { type: 'json' };
const TAG = 'Player';
@@ -18,7 +19,7 @@ interface SerializablePlayer {
sts: number;
sig_sc?: string;
nsig_sc?: string;
library_version: number;
library_version: string;
}
/**
@@ -199,10 +200,9 @@ export default class Player {
return null;
try {
const current_library_version = parseInt(Platform.shim.info.version.split('.')[0]);
const player_data = BinarySerializer.deserialize<SerializablePlayer>(new Uint8Array(buffer));
if (player_data.library_version !== current_library_version) {
if (player_data.library_version !== packageInfo.version) {
Log.warn(TAG, `Cached player data is from a different library version (${player_data.library_version}). Ignoring it.`);
return null;
}
@@ -224,14 +224,12 @@ export default class Player {
if (!cache || !this.sig_sc || !this.nsig_sc)
return;
const current_library_version = parseInt(Platform.shim.info.version.split('.')[0]);
const buffer = BinarySerializer.serialize({
player_id: this.player_id,
sts: this.sts,
sig_sc: this.sig_sc,
nsig_sc: this.nsig_sc,
library_version: current_library_version
library_version: packageInfo.version
});
await cache.set(this.player_id, buffer);
@@ -286,7 +284,7 @@ export default class Player {
if (!functions || !var_name)
Log.warn(TAG, 'Failed to extract signature decipher algorithm.');
return `${global_variable?.result || ''} function descramble_sig(${var_name}) { let ${obj_name}={${functions}}; ${match[2]} } descramble_sig(sig);`;
return `${global_variable?.result ? `var ${global_variable.result};` : ''} function descramble_sig(${var_name}) { let ${obj_name}={${functions}}; ${match[2]} } descramble_sig(sig);`;
}
static extractNSigSourceCode(data: string, ast?: ReturnType<typeof Jinter.parseScript>, global_variable?: ASTLookupResult): string | undefined {
@@ -303,7 +301,7 @@ export default class Player {
nsig_function = findFunction(data, { includes: '.reverse().forEach(function', ast });
if (nsig_function)
return `${global_variable.result} var ${nsig_function.result} ${nsig_function.name}(nsig);`;
return `var ${global_variable.result}; var ${nsig_function.result} ${nsig_function.name}(nsig);`;
}
// This is the suffix of the error tag.

View File

@@ -7,6 +7,7 @@ import {
generateRandomString, getRandomUserAgent,
InnertubeError, Platform, SessionError
} from '../utils/Utils.js';
import packageInfo from '../../package.json' with { type: 'json' };
import type { DeviceCategory } from '../utils/Utils.js';
import type { FetchFunction, ICache } from '../types/index.js';
@@ -332,7 +333,7 @@ export default class Session extends EventEmitter {
try {
const session_data = BinarySerializer.deserialize<SerializableSession>(new Uint8Array(buffer));
if (session_data.library_version !== parseInt(Platform.shim.info.version.split('.')[0])) {
if (session_data.library_version !== parseInt(packageInfo.version.split('.', 1)[0])) {
Log.warn(TAG, `Cached session data is from a different library version (${session_data.library_version}). Regenerating session data.`);
return null;
}
@@ -517,7 +518,7 @@ export default class Session extends EventEmitter {
const buffer = BinarySerializer.serialize({
...session_data,
library_version: parseInt(Platform.shim.info.version)
library_version: parseInt(packageInfo.version)
});
await cache.set('innertube_session_data', buffer);

View File

@@ -160,7 +160,7 @@ export default class Feed<T extends IParsedResponse = IParsedResponse> {
* Finds shelf by title.
*/
getShelf(title: string) {
return this.shelves.get({ title });
return this.shelves.find((shelf) => shelf.title.toString() === title);
}
/**

View File

@@ -188,8 +188,8 @@ export default class MediaInfo {
if (!next_response.engagement_panels)
throw new InnertubeError('Engagement panels not found. Video likely has no transcript.');
const transcript_panel = next_response.engagement_panels.get({
panel_identifier: 'engagement-panel-searchable-transcript'
const transcript_panel = next_response.engagement_panels.find((panel) => {
return panel.panel_identifier === 'engagement-panel-searchable-transcript';
});
if (!transcript_panel)

View File

@@ -5,7 +5,7 @@ import ChipView from './ChipView.js';
export default class ChipBarView extends YTNode {
static type = 'ChipBarView';
chips: ObservedArray<ChipView> | null;
chips: ObservedArray<ChipView>;
constructor(data: RawNode) {
super();

View File

@@ -22,7 +22,7 @@ export default class CompactVideo extends YTNode {
public short_byline_text?: Text;
public long_byline_text?: Text;
public published?: Text;
public badges: MetadataBadge[];
public badges: ObservedArray<MetadataBadge>;
public thumbnail_overlays: ObservedArray<YTNode>;
public endpoint?: NavigationEndpoint;
public menu: Menu | null;

View File

@@ -1,8 +1,10 @@
import type { ObservedArray } from '../helpers.js';
import { YTNode } from '../helpers.js';
import type { RawNode } from '../index.js';
import { Text } from '../misc.js';
import { Parser } from '../index.js';
import AvatarStackView from './AvatarStackView.js';
import BadgeView from './BadgeView.js';
export type MetadataRow = {
metadata_parts?: {
@@ -10,6 +12,7 @@ export type MetadataRow = {
avatar_stack: AvatarStackView | null;
enable_truncation?: boolean;
}[];
badges: ObservedArray<BadgeView>
};
export default class ContentMetadataView extends YTNode {
@@ -25,7 +28,8 @@ export default class ContentMetadataView extends YTNode {
text: part.text ? Text.fromAttributed(part.text) : null,
avatar_stack: Parser.parseItem(part.avatarStack, AvatarStackView),
enable_truncation: data.enableTruncation
}))
})),
badges: Parser.parseArray(row.badges, BadgeView)
}));
this.delimiter = data.delimiter;
}

View File

@@ -3,6 +3,7 @@ import { Parser, type RawNode } from '../index.js';
import DialogHeaderView from './DialogHeaderView.js';
import FormFooterView from './FormFooterView.js';
import CreatePlaylistDialogFormView from './CreatePlaylistDialogFormView.js';
import ListView from './ListView.js';
import PanelFooterView from './PanelFooterView.js';
export default class DialogView extends YTNode {
@@ -10,12 +11,12 @@ export default class DialogView extends YTNode {
public header: DialogHeaderView | null;
public footer: FormFooterView | PanelFooterView | null;
public custom_content: CreatePlaylistDialogFormView | null;
public custom_content: CreatePlaylistDialogFormView | ListView | null;
constructor (data: RawNode) {
super();
this.header = Parser.parseItem(data.header, DialogHeaderView);
this.footer = Parser.parseItem(data.footer, [ FormFooterView, PanelFooterView ]);
this.custom_content = Parser.parseItem(data.customContent, CreatePlaylistDialogFormView);
this.custom_content = Parser.parseItem(data.customContent, [ CreatePlaylistDialogFormView, ListView ]);
}
}

View File

@@ -14,7 +14,7 @@ export default class GridShow extends YTNode {
thumbnail_renderer: ShowCustomThumbnail | null;
endpoint: NavigationEndpoint;
long_byline_text: Text;
thumbnail_overlays: ObservedArray<ThumbnailOverlayBottomPanel> | null;
thumbnail_overlays: ObservedArray<ThumbnailOverlayBottomPanel>;
author: Author;
constructor(data: RawNode) {

View File

@@ -14,7 +14,7 @@ export default class InteractiveTabbedHeader extends YTNode {
title: Text;
description: Text;
metadata: Text;
badges: MetadataBadge[];
badges: ObservedArray<MetadataBadge>;
box_art: Thumbnail[];
banner: Thumbnail[];
buttons: ObservedArray<SubscribeButton | Button>;

View File

@@ -0,0 +1,32 @@
import type { ObservedArray } from '../helpers.js';
import type { RawNode } from '../types/RawResponse.js';
import { YTNode } from '../helpers.js';
import { Parser } from '../index.js';
import AvatarView from './AvatarView.js';
import RendererContext from './misc/RendererContext.js';
import SubscribeButtonView from './SubscribeButtonView.js';
import Text from './misc/Text.js';
export default class ListItemView extends YTNode {
static type = 'ListItemView';
public title: Text;
public subtitle: Text;
public leading_accessory: AvatarView | null;
public renderer_context: RendererContext;
public trailing_buttons?: ObservedArray<SubscribeButtonView>;
constructor(data: RawNode) {
super();
this.title = Text.fromAttributed(data.title);
this.subtitle = Text.fromAttributed(data.subtitle);
this.leading_accessory = Parser.parseItem(data.leadingAccessory, AvatarView);
this.renderer_context = new RendererContext(data.rendererContext);
if ('trailingButtons' in data) {
this.trailing_buttons = Parser.parseArray(data.trailingButtons.buttons, SubscribeButtonView);
}
}
}

View File

@@ -0,0 +1,18 @@
import type { ObservedArray } from '../helpers.js';
import type { RawNode } from '../types/RawResponse.js';
import { YTNode } from '../helpers.js';
import { Parser } from '../index.js';
import ListItemView from './ListItemView.js';
export default class ListView extends YTNode {
static type = 'ListView';
public items: ObservedArray<ListItemView>;
constructor(data: RawNode) {
super();
this.items = Parser.parseArray(data.listItems, ListItemView);
}
}

View File

@@ -1,6 +1,7 @@
import { YTNode } from '../helpers.js';
import { Parser, type RawNode } from '../index.js';
import ContentMetadataView from './ContentMetadataView.js';
import AvatarStackView from './AvatarStackView.js';
import DecoratedAvatarView from './DecoratedAvatarView.js';
import Text from './misc/Text.js';
import ButtonView from './ButtonView.js';
@@ -10,14 +11,14 @@ export default class LockupMetadataView extends YTNode {
public title: Text;
public metadata: ContentMetadataView | null;
public image: DecoratedAvatarView | null;
public image: DecoratedAvatarView | AvatarStackView | null;
public menu_button: ButtonView | null;
constructor(data: RawNode) {
super();
this.title = Text.fromAttributed(data.title);
this.metadata = Parser.parseItem(data.metadata, ContentMetadataView);
this.image = Parser.parseItem(data.image, DecoratedAvatarView);
this.image = Parser.parseItem(data.image, [ DecoratedAvatarView, AvatarStackView ]);
this.menu_button = Parser.parseItem(data.menuButton, ButtonView);
}
}

View File

@@ -10,7 +10,7 @@ export class Marker extends YTNode {
marker_key: string;
value: {
heatmap?: Heatmap | null;
chapters?: Chapter[];
chapters?: ObservedArray<Chapter>;
};
constructor(data: RawNode) {

View File

@@ -22,7 +22,7 @@ export default class MusicResponsiveHeader extends YTNode {
strapline_text_one: Text;
strapline_thumbnail: MusicThumbnail | null;
second_subtitle: Text;
subtitle_badge?: ObservedArray<MusicInlineBadge> | null;
subtitle_badge?: ObservedArray<MusicInlineBadge>;
description?: MusicDescriptionShelf | null;
constructor(data: RawNode) {

View File

@@ -30,7 +30,7 @@ export default class MusicResponsiveListItem extends YTNode {
item_type: 'album' | 'playlist' | 'artist' | 'library_artist' | 'non_music_track' | 'video' | 'song' | 'endpoint' | 'unknown' | 'podcast_show' | undefined;
index?: Text;
thumbnail?: MusicThumbnail | null;
badges;
badges?: ObservedArray<YTNode>;
menu?: Menu | null;
overlay?: MusicItemThumbnailOverlay | null;

View File

@@ -22,7 +22,7 @@ export default class Playlist extends YTNode {
menu: YTNode;
badges: ObservedArray<YTNode>;
endpoint: NavigationEndpoint;
thumbnail_overlays;
thumbnail_overlays: ObservedArray<YTNode>;
view_playlist?: Text;
constructor(data: RawNode) {

View File

@@ -1,11 +1,12 @@
import { Parser, type RawNode } from '../index.js';
import BackstageImage from './BackstageImage.js';
import type { ObservedArray } from '../helpers.js';
import { YTNode } from '../helpers.js';
export default class PostMultiImage extends YTNode {
static type = 'PostMultiImage';
images : BackstageImage[];
images: ObservedArray<BackstageImage>;
constructor(data: RawNode) {
super();

View File

@@ -0,0 +1,70 @@
import type { RawNode } from '../types/RawResponse.js';
import { YTNode } from '../helpers.js';
import NavigationEndpoint from './NavigationEndpoint.js';
interface ButtonContent {
button_text: string;
accessibility_text: string;
image_name: string;
subscribe_state_subscribed: boolean;
endpoint: NavigationEndpoint;
}
export default class SubscribeButtonView extends YTNode {
static type = 'SubscribeButtonView';
public subscribe_button_content: ButtonContent;
public unsubscribe_button_content: ButtonContent;
public disable_notification_bell: boolean;
public button_style: {
unsubscribed_state_style: string;
subscribed_state_style: string;
};
public is_signed_out: boolean;
public background_style: string;
public disable_subscribe_button: boolean;
public on_show_subscription_options: NavigationEndpoint;
public channel_id: string;
public enable_subscribe_button_post_click_animation: boolean;
public bell_accessiblity_data: {
off_label: string;
all_label: string;
occasional_label: string;
disabled_label: string;
};
constructor(data: RawNode) {
super();
this.subscribe_button_content = this.#parseButtonContent(data.subscribeButtonContent);
this.unsubscribe_button_content = this.#parseButtonContent(data.unsubscribeButtonContent);
this.disable_notification_bell = data.disableNotificationBell;
this.button_style = {
unsubscribed_state_style: data.buttonStyle.unsubscribedStateStyle,
subscribed_state_style: data.buttonStyle.subscribedStateStyle
};
this.is_signed_out = data.isSignedOut;
this.background_style = data.backgroundStyle;
this.disable_subscribe_button = data.disableSubscribeButton;
this.on_show_subscription_options = new NavigationEndpoint(data.onShowSubscriptionOptions);
this.channel_id = data.channelId;
this.enable_subscribe_button_post_click_animation = data.enableSubscribeButtonPostClickAnimation;
this.bell_accessiblity_data = {
off_label: data.bellAccessibilityData.offLabel,
all_label: data.bellAccessibilityData.allLabel,
occasional_label: data.bellAccessibilityData.occasionalLabel,
disabled_label: data.bellAccessibilityData.disabledLabel
};
}
#parseButtonContent(data: RawNode): ButtonContent {
return {
button_text: data.buttonText,
accessibility_text: data.accessibilityText,
image_name: data.imageName,
subscribe_state_subscribed: data.subscribeState.subscribed,
endpoint: new NavigationEndpoint(data.onTapCommand)
};
}
}

View File

@@ -1,3 +1,4 @@
import type { ObservedArray } from '../helpers.js';
import { YTNode } from '../helpers.js';
import { Parser, type RawNode } from '../index.js';
import ThumbnailBadgeView from './ThumbnailBadgeView.js';
@@ -5,7 +6,7 @@ import ThumbnailBadgeView from './ThumbnailBadgeView.js';
export default class ThumbnailOverlayBadgeView extends YTNode {
static type = 'ThumbnailOverlayBadgeView';
public badges: ThumbnailBadgeView[];
public badges: ObservedArray<ThumbnailBadgeView>;
public position: string;
constructor(data: RawNode) {

View File

@@ -20,7 +20,7 @@ export default class TwoColumnWatchNextResults extends YTNode {
id: string,
title: string,
author: Text | Author,
contents: YTNode[],
contents: ObservedArray<YTNode>,
current_index: number,
is_infinite: boolean,
menu: Menu | null

View File

@@ -24,7 +24,7 @@ export default class Video extends YTNode {
public thumbnail_overlays: ObservedArray<YTNode>;
public rich_thumbnail?: YTNode;
public author: Author;
public badges: MetadataBadge[];
public badges: ObservedArray<MetadataBadge>;
public endpoint?: NavigationEndpoint;
public published?: Text;
public view_count?: Text;

View File

@@ -6,7 +6,7 @@ import { YTNode } from '../../helpers.js';
export default class AppendContinuationItemsAction extends YTNode {
static type = 'AppendContinuationItemsAction';
contents: ObservedArray<YTNode> | null;
contents: ObservedArray<YTNode>;
target: string;
constructor(data: RawNode) {

View File

@@ -12,7 +12,7 @@ export default class LiveChatSponsorshipsHeader extends YTNode {
author_name: Text;
author_photo: Thumbnail[];
author_badges: ObservedArray<LiveChatAuthorBadge> | null;
author_badges: ObservedArray<LiveChatAuthorBadge>;
primary_text: Text;
menu_endpoint: NavigationEndpoint;
context_menu_accessibility_label: string;

View File

@@ -1,3 +1,4 @@
import type { ObservedArray } from '../../helpers.js';
import { YTNode } from '../../helpers.js';
import Text from '../misc/Text.js';
import { Parser, type RawNode } from '../../index.js';
@@ -6,7 +7,7 @@ export default class MobileTopbar extends YTNode {
static type = 'MobileTopbar';
public placeholder_text: Text;
public buttons;
public buttons: ObservedArray<YTNode>;
public logo_type?: string;
constructor(data: RawNode) {

View File

@@ -1,10 +1,11 @@
import type { ObservedArray } from '../../helpers.js';
import { YTNode } from '../../helpers.js';
import { Parser, type RawNode } from '../../index.js';
export default class PivotBar extends YTNode {
static type = 'PivotBar';
public items;
public items: ObservedArray<YTNode>;
constructor(data: RawNode) {
super();

View File

@@ -9,7 +9,7 @@ import type { ObservedArray } from './helpers.js';
export class ItemSectionContinuation extends YTNode {
static readonly type = 'itemSectionContinuation';
contents: ObservedArray<YTNode> | null;
contents: ObservedArray<YTNode>;
continuation?: string;
constructor(data: RawNode) {
@@ -94,7 +94,7 @@ export class MusicShelfContinuation extends YTNode {
static readonly type = 'musicShelfContinuation';
continuation: string;
contents: ObservedArray<YTNode> | null;
contents: ObservedArray<YTNode>;
constructor(data: RawNode) {
super();
@@ -126,7 +126,7 @@ export class PlaylistPanelContinuation extends YTNode {
static readonly type = 'playlistPanelContinuation';
continuation: string;
contents: ObservedArray<YTNode> | null;
contents: ObservedArray<YTNode>;
constructor(data: RawNode) {
super();

View File

@@ -224,6 +224,8 @@ export { default as ItemSectionTab } from './classes/ItemSectionTab.js';
export { default as ItemSectionTabbedHeader } from './classes/ItemSectionTabbedHeader.js';
export { default as LikeButton } from './classes/LikeButton.js';
export { default as LikeButtonView } from './classes/LikeButtonView.js';
export { default as ListItemView } from './classes/ListItemView.js';
export { default as ListView } from './classes/ListView.js';
export { default as LiveChat } from './classes/LiveChat.js';
export { default as AddBannerToLiveChatCommand } from './classes/livechat/AddBannerToLiveChatCommand.js';
export { default as AddChatItemAction } from './classes/livechat/AddChatItemAction.js';
@@ -457,6 +459,7 @@ export { default as StructuredDescriptionPlaylistLockup } from './classes/Struct
export { default as SubFeedOption } from './classes/SubFeedOption.js';
export { default as SubFeedSelector } from './classes/SubFeedSelector.js';
export { default as SubscribeButton } from './classes/SubscribeButton.js';
export { default as SubscribeButtonView } from './classes/SubscribeButtonView.js';
export { default as SubscriptionNotificationToggleButton } from './classes/SubscriptionNotificationToggleButton.js';
export { default as Tab } from './classes/Tab.js';
export { default as Tabbed } from './classes/Tabbed.js';

View File

@@ -1,10 +1,11 @@
import * as YTNodes from './nodes.js';
import { InnertubeError, ParsingError, Platform } from '../utils/Utils.js';
import { InnertubeError, ParsingError } from '../utils/Utils.js';
import type { ObservedArray, YTNode, YTNodeConstructor } from './helpers.js';
import { Memo, observe, SuperParsedResult } from './helpers.js';
import type { KeyInfo } from './generator.js';
import { camelToSnake, generateRuntimeClass, generateTypescriptClass } from './generator.js';
import { Log } from '../utils/index.js';
import packageInfo from '../../package.json' with { type: 'json' };
import {
Continuation,
@@ -102,7 +103,7 @@ let ERROR_HANDLER: ParserErrorHandler = ({ classname, ...context }: ParserError)
Log.warn(TAG,
new InnertubeError(
`Something went wrong at ${classname}!\n` +
`This is a bug, please report it at ${Platform.shim.info.bugs_url}`, {
`This is a bug, please report it at ${packageInfo.bugs.url}`, {
stack: context.error.stack,
classdata: JSON.stringify(context.classdata, null, 2)
}
@@ -122,7 +123,7 @@ let ERROR_HANDLER: ParserErrorHandler = ({ classname, ...context }: ParserError)
Log.warn(TAG,
new InnertubeError(
`Mutation data required for processing ${classname}, but none found.\n` +
`This is a bug, please report it at ${Platform.shim.info.bugs_url}`
`This is a bug, please report it at ${packageInfo.bugs.url}`
)
);
break;
@@ -131,7 +132,7 @@ let ERROR_HANDLER: ParserErrorHandler = ({ classname, ...context }: ParserError)
new InnertubeError(
`Mutation data missing or invalid for ${context.failed} out of ${context.total} MusicMultiSelectMenuItems. ` +
`The titles of the failed items are: ${context.titles.join(', ')}.\n` +
`This is a bug, please report it at ${Platform.shim.info.bugs_url}`
`This is a bug, please report it at ${packageInfo.bugs.url}`
)
);
break;
@@ -139,7 +140,7 @@ let ERROR_HANDLER: ParserErrorHandler = ({ classname, ...context }: ParserError)
Log.warn(TAG,
new InnertubeError(
`${classname} not found!\n` +
`This is a bug, want to help us fix it? Follow the instructions at ${Platform.shim.info.repo_url}/blob/main/docs/updating-the-parser.md or report it at ${Platform.shim.info.bugs_url}!\n` +
`This is a bug, want to help us fix it? Follow the instructions at ${packageInfo.homepage.split('#', 1)[0]}/blob/main/docs/updating-the-parser.md or report it at ${packageInfo.bugs.url}!\n` +
`Introspected and JIT generated this class in the meantime:\n${generateTypescriptClass(classname, context.key_info)}`
)
);

View File

@@ -62,7 +62,7 @@ export default class Channel extends TabbedFeed<IBrowseResponse> {
this.subscribe_button = this.page.header_memo?.getType(SubscribeButton)[0];
if (this.page.contents)
this.current_tab = this.page.contents.item().as(TwoColumnBrowseResults).tabs.get({ selected: true });
this.current_tab = this.page.contents.item().as(TwoColumnBrowseResults).tabs.find((tab) => tab.selected);
}
/**
@@ -75,7 +75,7 @@ export default class Channel extends TabbedFeed<IBrowseResponse> {
const filter_chipbar = this.memo.getType(FeedFilterChipBar)[0];
if (typeof filter === 'string') {
target_filter = filter_chipbar?.contents.get({ text: filter });
target_filter = filter_chipbar?.contents.find((chip) => chip.text === filter);
if (!target_filter)
throw new InnertubeError(`Filter ${filter} not found`, { available_filters: this.filters });
} else {
@@ -330,7 +330,7 @@ export class FilteredChannelList extends FilterableFeed<IBrowseResponse> {
constructor(actions: Actions, data: ApiResponse | IBrowseResponse, already_parsed = false) {
super(actions, data, already_parsed);
this.applied_filter = this.memo.getType(ChipCloudChip).get({ is_selected: true });
this.applied_filter = this.memo.getType(ChipCloudChip).find((chip) => chip.is_selected);
// Removes the filter chipbar from the actions list
if (

View File

@@ -59,7 +59,9 @@ export default class Search extends Feed<ISearchResponse> {
if (typeof card === 'string') {
if (!this.refinement_cards) throw new InnertubeError('No refinement cards found.');
target_card = this.refinement_cards?.cards.get({ query: card })?.as(SearchRefinementCard);
target_card = this.refinement_cards?.cards.find((refinement_card): refinement_card is SearchRefinementCard => {
return refinement_card.is(SearchRefinementCard) && refinement_card.query === card;
});
if (!target_card)
throw new InnertubeError(`Refinement card "${card}" not found`, { available_cards: this.refinement_card_queries });
} else if (card.type === 'SearchRefinementCard') {

View File

@@ -33,7 +33,7 @@ export default class Settings {
if (!this.#page.contents)
throw new InnertubeError('Page contents not found');
const tab = this.#page.contents.item().as(TwoColumnBrowseResults).tabs.get({ selected: true });
const tab = this.#page.contents.item().as(TwoColumnBrowseResults).tabs.find((tab) => tab.selected);
if (!tab)
throw new InnertubeError('Target tab not found');
@@ -58,7 +58,7 @@ export default class Settings {
let item: CompactLink | undefined;
if (typeof target_item === 'string') {
item = this.sidebar.items.get({ title: target_item });
item = this.sidebar.items.find((link) => link.title === target_item);
if (!item)
throw new InnertubeError(`Item "${target_item}" not found`, { available_items: this.sidebar_items });
} else if (target_item?.is(CompactLink)) {

View File

@@ -131,7 +131,9 @@ export default class VideoInfo extends MediaInfo {
}
}
const comments_entry_point = results.get({ target_id: 'comments-entry-point' })?.as(ItemSection);
const comments_entry_point = results.find((node): node is ItemSection => {
return node.is(ItemSection) && node.target_id === 'comments-entry-point';
});
this.comments_entry_point_header = comments_entry_point?.contents?.firstOfType(CommentsEntryPointHeader);
this.livechat = next?.contents_memo?.getType(LiveChat)[0];
@@ -163,7 +165,7 @@ export default class VideoInfo extends MediaInfo {
let cloud_chip: ChipCloudChip;
if (typeof target_filter === 'string') {
const filter = this.related_chip_cloud?.chips?.get({ text: target_filter });
const filter = this.related_chip_cloud?.chips?.find((chip) => chip.text === target_filter);
if (!filter)
throw new InnertubeError('Invalid filter', { available_filters: this.filters });
@@ -178,9 +180,11 @@ export default class VideoInfo extends MediaInfo {
if (cloud_chip.is_selected) return this;
const response = await cloud_chip.endpoint?.call(this.actions, { parse: true });
const data = response?.on_response_received_endpoints?.get({ target_id: 'watch-next-feed' });
const data = response?.on_response_received_endpoints?.find((endpoint): endpoint is ReloadContinuationItemsCommand => {
return endpoint.is(ReloadContinuationItemsCommand) && endpoint.target_id === 'watch-next-feed';
});
this.watch_next_feed = data?.as(AppendContinuationItemsAction, ReloadContinuationItemsCommand).contents;
this.watch_next_feed = data?.contents;
return this;
}
@@ -207,12 +211,12 @@ export default class VideoInfo extends MediaInfo {
throw new InnertubeError('Watch next feed continuation not found');
const response = await this.#watch_next_continuation?.endpoint.call(this.actions, { parse: true });
const data = response?.on_response_received_endpoints?.get({ type: 'AppendContinuationItemsAction' });
const data = response?.on_response_received_endpoints?.firstOfType(AppendContinuationItemsAction);
if (!data)
throw new InnertubeError('AppendContinuationItemsAction not found');
this.watch_next_feed = data?.as(AppendContinuationItemsAction, ReloadContinuationItemsCommand).contents;
this.watch_next_feed = data?.contents;
if (this.watch_next_feed?.at(-1)?.is(ContinuationItem)) {
this.#watch_next_continuation = this.watch_next_feed.pop()?.as(ContinuationItem);
} else {

View File

@@ -20,7 +20,7 @@ export default class Explore {
constructor(response: ApiResponse) {
this.#page = Parser.parseResponse<IBrowseResponse>(response.data);
const tab = this.#page.contents?.item().as(SingleColumnBrowseResults).tabs.get({ selected: true });
const tab = this.#page.contents?.item().as(SingleColumnBrowseResults).tabs.find((tab) => tab.selected);
if (!tab)
throw new InnertubeError('Could not find target tab.');

View File

@@ -23,7 +23,7 @@ export default class HomeFeed {
this.#actions = actions;
this.#page = Parser.parseResponse<IBrowseResponse>(response.data);
const tab = this.#page.contents?.item().as(SingleColumnBrowseResults).tabs.get({ selected: true });
const tab = this.#page.contents?.item().as(SingleColumnBrowseResults).tabs.find((tab) => tab.selected);
if (!tab)
throw new InnertubeError('Could not find Home tab.');
@@ -62,7 +62,7 @@ export default class HomeFeed {
let cloud_chip: ChipCloudChip | undefined;
if (typeof target_filter === 'string') {
cloud_chip = this.header?.chips?.as(ChipCloudChip).get({ text: target_filter });
cloud_chip = this.header?.chips?.as(ChipCloudChip).find((chip) => chip.text === target_filter);
if (!cloud_chip)
throw new InnertubeError('Could not find filter with given name.', { available_filters: this.filters });
} else if (target_filter?.is(ChipCloudChip)) {

View File

@@ -97,7 +97,7 @@ export default class Library {
const chip_cloud = this.#page.contents_memo?.getType(ChipCloud)[0];
if (typeof filter === 'string') {
target_chip = chip_cloud?.chips.get({ text: filter });
target_chip = chip_cloud?.chips.find((chip) => chip.text === filter);
if (!target_chip)
throw new InnertubeError(`Filter "${filter}" not found`, { available_filters: this.filters });

View File

@@ -32,7 +32,7 @@ export default class Search {
if (!this.#page.contents || !this.#page.contents_memo)
throw new InnertubeError('Response did not contain any contents.');
const tab = this.#page.contents.item().as(TabbedSearchResults).tabs.get({ selected: true });
const tab = this.#page.contents.item().as(TabbedSearchResults).tabs.find((tab) => tab.selected);
if (!tab)
throw new InnertubeError('Could not find target tab.');
@@ -87,7 +87,7 @@ export default class Search {
let cloud_chip: ChipCloudChip | undefined;
if (typeof target_filter === 'string') {
cloud_chip = this.header?.chips?.as(ChipCloudChip).get({ text: target_filter });
cloud_chip = this.header?.chips?.as(ChipCloudChip).find((chip) => chip.text === target_filter);
if (!cloud_chip)
throw new InnertubeError('Could not find filter with given name.', { available_filters: this.filters });
} else if (target_filter?.is(ChipCloudChip)) {

View File

@@ -48,7 +48,7 @@ class TrackInfo extends MediaInfo {
throw new InnertubeError('Could not find any tab');
const target_tab =
this.tabs.get({ title: title_or_page_type }) ||
this.tabs.find((tab) => tab.title === title_or_page_type) ||
this.tabs.find((tab) => tab.endpoint.payload.browseEndpointContextSupportedConfigs?.browseEndpointContextMusicConfig?.pageType === title_or_page_type) ||
this.tabs?.[0];

View File

@@ -1,12 +1,8 @@
import type { ICache } from '../types/Cache.js';
import { Platform } from '../utils/Utils.js';
import evaluate from './jsruntime/jinter.js';
import { $INLINE_JSON } from 'ts-transformer-inline-file';
import sha1Hash from './polyfills/web-crypto.js';
const { homepage, version, bugs } = $INLINE_JSON('../../package.json');
const repo_url = homepage?.split('#')[0];
class Cache implements ICache {
#persistent_directory: string;
#persistent: boolean;
@@ -44,11 +40,6 @@ class Cache implements ICache {
Platform.load({
runtime: 'cf-worker',
info: {
version: version,
bugs_url: bugs?.url || `${repo_url}/issues`,
repo_url
},
server: true,
Cache: Cache,
sha1Hash,

View File

@@ -3,7 +3,6 @@ import type { ICache } from '../types/Cache.js';
import { Platform } from '../utils/Utils.js';
import evaluate from './jsruntime/jinter.js';
import sha1Hash from './polyfills/web-crypto.js';
import package_json from '../../package.json' with { type: 'json' };
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
@@ -82,11 +81,6 @@ class Cache implements ICache {
Platform.load({
runtime: 'deno',
info: {
version: package_json.version,
bugs_url: package_json.bugs.url,
repo_url: package_json.homepage.split('#')[0]
},
server: true,
Cache: Cache,
sha1Hash,

View File

@@ -18,15 +18,11 @@ import fs from 'fs/promises';
import CustomEvent from './polyfills/node-custom-event.js';
import { fileURLToPath } from 'url';
import evaluate from './jsruntime/jinter.js';
import { $INLINE_JSON } from 'ts-transformer-inline-file';
const meta_url = import.meta.url;
const is_cjs = !meta_url;
const __dirname__ = is_cjs ? __dirname : path.dirname(fileURLToPath(meta_url));
const { homepage, version, bugs } = $INLINE_JSON('../../package.json');
const repo_url = homepage?.split('#')[0];
class Cache implements ICache {
#persistent_directory: string;
#persistent: boolean;
@@ -100,11 +96,6 @@ class Cache implements ICache {
Platform.load({
runtime: 'node',
info: {
version: version,
bugs_url: bugs?.url || `${repo_url}/issues`,
repo_url
},
server: true,
Cache: Cache,
sha1Hash: async (data: string) => {

View File

@@ -2,7 +2,6 @@
import type { ICache } from '../types/Cache.js';
import { Platform } from '../utils/Utils.js';
import sha1Hash from './polyfills/web-crypto.js';
import package_json from '../../package.json' with { type: 'json' };
import evaluate from './jsruntime/jinter.js';
class Cache implements ICache {
@@ -42,11 +41,6 @@ class Cache implements ICache {
Platform.load({
runtime: 'react-native',
server: false,
info: {
version: package_json.version,
bugs_url: package_json.bugs.url,
repo_url: package_json.homepage.split('#')[0]
},
Cache: Cache,
sha1Hash,
uuidv4() {

View File

@@ -2,7 +2,6 @@
import type { ICache } from '../types/Cache.js';
import { Platform } from '../utils/Utils.js';
import sha1Hash from './polyfills/web-crypto.js';
import package_json from '../../package.json' assert { type: 'json' };
import evaluate from './jsruntime/jinter.js';
import * as Log from '../utils/Log.js';
@@ -95,11 +94,6 @@ class Cache implements ICache {
Platform.load({
runtime: 'browser',
server: false,
info: {
version: package_json.version,
bugs_url: package_json.bugs.url,
repo_url: package_json.homepage.split('#')[0]
},
Cache: Cache,
sha1Hash,
uuidv4() {

View File

@@ -8,11 +8,6 @@ export type VMPrimative = string | number | boolean | null | undefined;
interface PlatformShim {
runtime: Runtime;
info: {
version: string,
bugs_url: string,
repo_url: string
},
server: boolean;
Cache: ICacheConstructor;
sha1Hash(data: string): Promise<string>;

View File

@@ -1,8 +1,9 @@
import type { StoryboardData } from '../parser/classes/PlayerStoryboardSpec.js';
import PlayerStoryboardSpec from '../parser/classes/PlayerStoryboardSpec.js';
import { getStringBetweenStrings, InnertubeError, Platform } from './Utils.js';
import { getStringBetweenStrings, InnertubeError } from './Utils.js';
import * as Constants from './Constants.js';
import * as Log from './Log.js';
import packageInfo from '../../package.json' with { type: 'json' };
import type Actions from '../core/Actions.js';
import type Player from '../core/Player.js';
@@ -543,7 +544,7 @@ function getColorInfo(format: Format) {
anonymisedFormat.cipher = 'REDACTED';
Log.warn(TAG_, `Unknown matrix coefficients "${color_info.matrix_coefficients}". The DASH manifest is still usable without this.\n`
+ `Please report it at ${Platform.shim.info.bugs_url} so we can add support for it.\n`
+ `Please report it at ${packageInfo.bugs.url} so we can add support for it.\n`
+ `InnerTube client: ${url.searchParams.get('c')}\nformat:`, anonymisedFormat);
}
}

View File

@@ -9,6 +9,7 @@ import { Memo } from '../parser/helpers.js';
import { Text } from '../parser/misc.js';
import * as Log from './Log.js';
import userAgents from './user-agents.js';
import packageInfo from '../../package.json' with { type: 'json' };
const TAG_ = 'Utils';
@@ -40,7 +41,7 @@ export class InnertubeError extends Error {
}
this.date = new Date();
this.version = Platform.shim.info.version;
this.version = packageInfo.version;
}
}
@@ -353,7 +354,7 @@ export function findFunction(source: string, args: ASTLookupArgs): ASTLookupResu
}
/**
* Searches for a variable declaration in the given code based on specified criteria.
* Searches for a variable declarator in the given code based on specified criteria.
*
* @example
* ```ts
@@ -381,23 +382,21 @@ export function findVariable(code: string, options: ASTLookupArgs): ASTLookupRes
function walk(node: Node): void {
if (found) return;
if (node.type === 'VariableDeclaration') {
if (node.type === 'VariableDeclarator') {
const [ start, end ] = node.range!;
const node_source = code.slice(start, end);
for (const declarator of node.declarations) {
if (declarator.id.type === 'Identifier') {
const var_name = declarator.id.name;
if (options.name && var_name === options.name) {
found = { start, end, name: var_name, node, result: node_source };
return;
}
if (node.id.type === 'Identifier') {
const var_name = node.id.name;
if (options.name && var_name === options.name) {
found = { start, end, name: var_name, node, result: node_source };
return;
}
}
if (
(options.includes && node_source.includes(options.includes)) ||
(options.regexp && options.regexp.test(node_source))) {
found = { start, end, name: (node.declarations?.[0]?.id as any)?.name, node, result: node_source };
found = { start, end, name: (node?.id as any)?.name, node, result: node_source };
return;
}
}

View File

@@ -100,7 +100,6 @@
// "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
"skipLibCheck": true, /* Skip type checking all .d.ts files. */
"plugins": [
{ "transform": "ts-transformer-inline-file/transformer" },
{ "transform": "./dev-scripts/enum-optimising-transformer.cjs" }
]
},