chore: add DOM.AsyncIterable lib and use standard filesystem types
Add `DOM.AsyncIterable` to the shared TypeScript configuration in `@backstage/cli`, making standard async iteration methods available on DOM APIs like `FileSystemDirectoryHandle`. This aligns behavior with TypeScript 6.0, where this lib is included in `DOM` by default. With the async iterable types now available, replace the custom `IterableDirectoryHandle` and `WritableFileHandle` types in the scaffolder plugin with the standard `FileSystemDirectoryHandle` and `FileSystemFileHandle` DOM types. Add type guard functions for `FileSystemHandle` since it is not a discriminated union. Signed-off-by: Jon Koops <jonkoops@gmail.com>
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/cli': patch
|
||||
---
|
||||
|
||||
Added `DOM.AsyncIterable` to the default `lib` in the shared TypeScript configuration, enabling standard async iteration support for DOM APIs such as `FileSystemDirectoryHandle`. This aligns behavior with [TypeScript 6.0](https://devblogs.microsoft.com/typescript/announcing-typescript-6-0/#the-dom-lib-now-contains-domiterable-and-domasynciterable), where this lib is included in `DOM` by default.
|
||||
@@ -0,0 +1,5 @@
|
||||
---
|
||||
'@backstage/plugin-scaffolder': patch
|
||||
---
|
||||
|
||||
Removed custom `IterableDirectoryHandle` and `WritableFileHandle` types in favor of the standard DOM `FileSystemDirectoryHandle` and `FileSystemFileHandle` types, which are now available through the `DOM.AsyncIterable` lib added to the shared TypeScript configuration.
|
||||
@@ -12,7 +12,7 @@
|
||||
"incremental": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "react",
|
||||
"lib": ["DOM", "DOM.Iterable", "ScriptHost", "ES2023"],
|
||||
"lib": ["DOM", "DOM.Iterable", "DOM.AsyncIterable", "ScriptHost", "ES2023"],
|
||||
"module": "ES2020",
|
||||
"moduleResolution": "bundler",
|
||||
"noEmit": false,
|
||||
|
||||
+4
-7
@@ -21,10 +21,7 @@ import {
|
||||
TemplateDirectoryAccess,
|
||||
WebFileSystemStore,
|
||||
} from '../../../lib/filesystem';
|
||||
import {
|
||||
IterableDirectoryHandle,
|
||||
WebFileSystemAccess,
|
||||
} from '../../../lib/filesystem/WebFileSystemAccess';
|
||||
import { WebFileSystemAccess } from '../../../lib/filesystem/WebFileSystemAccess';
|
||||
|
||||
jest.mock('../../../lib/filesystem/createExampleTemplate');
|
||||
|
||||
@@ -49,7 +46,7 @@ describe('useTemplateDirectory', () => {
|
||||
});
|
||||
|
||||
it('should return an access when there is existing directory in the file system store', async () => {
|
||||
const handle = {} as IterableDirectoryHandle;
|
||||
const handle = {} as FileSystemDirectoryHandle;
|
||||
|
||||
jest.spyOn(WebFileSystemStore, 'getDirectory').mockResolvedValue(handle);
|
||||
|
||||
@@ -65,7 +62,7 @@ describe('useTemplateDirectory', () => {
|
||||
const handle = {};
|
||||
jest
|
||||
.spyOn(WebFileSystemStore, 'getDirectory')
|
||||
.mockResolvedValue(handle as IterableDirectoryHandle);
|
||||
.mockResolvedValue(handle as FileSystemDirectoryHandle);
|
||||
const setDirectory = jest
|
||||
.spyOn(WebFileSystemStore, 'setDirectory')
|
||||
.mockResolvedValue(undefined);
|
||||
@@ -92,7 +89,7 @@ describe('useTemplateDirectory', () => {
|
||||
(createExampleTemplate as jest.Mock).mockResolvedValue(handle);
|
||||
jest
|
||||
.spyOn(WebFileSystemStore, 'getDirectory')
|
||||
.mockResolvedValue(handle as IterableDirectoryHandle);
|
||||
.mockResolvedValue(handle as FileSystemDirectoryHandle);
|
||||
const setDirectory = jest
|
||||
.spyOn(WebFileSystemStore, 'setDirectory')
|
||||
.mockResolvedValue(undefined);
|
||||
|
||||
@@ -16,30 +16,27 @@
|
||||
|
||||
import { TemplateDirectoryAccess, TemplateFileAccess } from './types';
|
||||
|
||||
type WritableFileHandle = FileSystemFileHandle & {
|
||||
createWritable(): Promise<{
|
||||
write(data: string | Blob | BufferSource): Promise<void>;
|
||||
close(): Promise<void>;
|
||||
}>;
|
||||
};
|
||||
function isFileHandle(
|
||||
handle: FileSystemHandle,
|
||||
): handle is FileSystemFileHandle {
|
||||
return handle.kind === 'file';
|
||||
}
|
||||
|
||||
// A nicer type than the one from the TS lib
|
||||
export interface IterableDirectoryHandle extends FileSystemDirectoryHandle {
|
||||
values(): AsyncIterable<
|
||||
| ({ kind: 'file' } & WritableFileHandle)
|
||||
| ({ kind: 'directory' } & IterableDirectoryHandle)
|
||||
>;
|
||||
function isDirectoryHandle(
|
||||
handle: FileSystemHandle,
|
||||
): handle is FileSystemDirectoryHandle {
|
||||
return handle.kind === 'directory';
|
||||
}
|
||||
|
||||
const showDirectoryPicker = (window as any).showDirectoryPicker as
|
||||
| (() => Promise<IterableDirectoryHandle>)
|
||||
| (() => Promise<FileSystemDirectoryHandle>)
|
||||
| undefined;
|
||||
|
||||
class WebFileAccess implements TemplateFileAccess {
|
||||
readonly path: string;
|
||||
private readonly handle: WritableFileHandle;
|
||||
private readonly handle: FileSystemFileHandle;
|
||||
|
||||
constructor(path: string, handle: WritableFileHandle) {
|
||||
constructor(path: string, handle: FileSystemFileHandle) {
|
||||
this.path = path;
|
||||
this.handle = handle;
|
||||
}
|
||||
@@ -57,9 +54,9 @@ class WebFileAccess implements TemplateFileAccess {
|
||||
|
||||
/** @internal */
|
||||
export class WebDirectoryAccess implements TemplateDirectoryAccess {
|
||||
private readonly handle: IterableDirectoryHandle;
|
||||
private readonly handle: FileSystemDirectoryHandle;
|
||||
|
||||
constructor(handle: IterableDirectoryHandle) {
|
||||
constructor(handle: FileSystemDirectoryHandle) {
|
||||
this.handle = handle;
|
||||
}
|
||||
|
||||
@@ -72,13 +69,13 @@ export class WebDirectoryAccess implements TemplateDirectoryAccess {
|
||||
}
|
||||
|
||||
private async *listDirectoryContents(
|
||||
dirHandle: IterableDirectoryHandle,
|
||||
dirHandle: FileSystemDirectoryHandle,
|
||||
basePath: string[] = [],
|
||||
): AsyncIterable<TemplateFileAccess> {
|
||||
for await (const handle of dirHandle.values()) {
|
||||
if (handle.kind === 'file') {
|
||||
if (isFileHandle(handle)) {
|
||||
yield new WebFileAccess([...basePath, handle.name].join('/'), handle);
|
||||
} else if (handle.kind === 'directory') {
|
||||
} else if (isDirectoryHandle(handle)) {
|
||||
// Skip git storage directory
|
||||
if (handle.name === '.git') {
|
||||
continue;
|
||||
@@ -116,7 +113,7 @@ export class WebFileSystemAccess {
|
||||
return Boolean(showDirectoryPicker);
|
||||
}
|
||||
|
||||
static fromHandle(handle: IterableDirectoryHandle) {
|
||||
static fromHandle(handle: FileSystemDirectoryHandle) {
|
||||
return new WebDirectoryAccess(handle);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,12 +16,11 @@
|
||||
|
||||
import { get, set } from 'idb-keyval';
|
||||
import { TemplateDirectoryAccess } from './types';
|
||||
import { IterableDirectoryHandle } from './WebFileSystemAccess';
|
||||
|
||||
export class WebFileSystemStore {
|
||||
private static readonly key = 'scalfolder-template-editor-directory';
|
||||
|
||||
static async getDirectory(): Promise<IterableDirectoryHandle | undefined> {
|
||||
static async getDirectory(): Promise<FileSystemDirectoryHandle | undefined> {
|
||||
const directory = await get(WebFileSystemStore.key);
|
||||
return directory.handle;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user