scaffolder: add filesystem access helpers

Signed-off-by: Patrik Oldsberg <poldsberg@gmail.com>
This commit is contained in:
Patrik Oldsberg
2022-04-04 10:15:27 +02:00
parent f45a2abcf2
commit c3a24968b3
4 changed files with 184 additions and 0 deletions
@@ -0,0 +1,105 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import {
TemplateFileSystemAccess,
TemplateDirectoryAccess,
TemplateFileAccess,
} from './types';
type WritableFileHandle = FileSystemFileHandle & {
createWritable(): Promise<{
write(data: string | Blob | BufferSource): Promise<void>;
close(): Promise<void>;
}>;
};
// A nicer type than the one from the TS lib
interface IterableDirectoryHandle extends FileSystemDirectoryHandle {
values(): AsyncIterable<
| ({ kind: 'file' } & WritableFileHandle)
| ({ kind: 'directory' } & IterableDirectoryHandle)
>;
}
const showDirectoryPicker = (window as any).showDirectoryPicker as
| (() => Promise<IterableDirectoryHandle>)
| undefined;
class WebFileAccess implements TemplateFileAccess {
constructor(
readonly path: string,
private readonly handle: WritableFileHandle,
) {}
file(): Promise<File> {
return this.handle.getFile();
}
async save(data: string | Blob | BufferSource): Promise<void> {
const writable = await this.handle.createWritable();
await writable.write(data);
await writable.close();
}
}
class WebDirectoryAccess implements TemplateDirectoryAccess {
constructor(private readonly handle: IterableDirectoryHandle) {}
async listFiles(): Promise<TemplateFileAccess[]> {
const content = [];
for await (const entry of this.listDirectoryContents(this.handle)) {
content.push(entry);
}
return content;
}
private async *listDirectoryContents(
dirHandle: IterableDirectoryHandle,
basePath: string[] = [],
): AsyncIterable<TemplateFileAccess> {
for await (const handle of dirHandle.values()) {
if (handle.kind === 'file') {
yield new WebFileAccess([...basePath, handle.name].join('/'), handle);
} else if (handle.kind === 'directory') {
yield* this.listDirectoryContents(handle, [...basePath, handle.name]);
}
}
}
}
/** @internal */
export class WebFileSystemAccess implements TemplateFileSystemAccess {
private static instance = new WebFileSystemAccess();
static get(): TemplateFileSystemAccess {
return this.instance;
}
static isSupported(): boolean {
return Boolean(showDirectoryPicker);
}
private constructor() {}
async requestDirectoryAccess(): Promise<TemplateDirectoryAccess> {
if (!showDirectoryPicker) {
throw new Error('File system access is not supported');
}
const handle = await showDirectoryPicker();
return new WebDirectoryAccess(handle);
}
}
@@ -0,0 +1,27 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export async function blobToBase64(file: Blob) {
const reader = new FileReader();
reader.readAsDataURL(file);
return new Promise<string>((resolve, reject) => {
reader.onload = () => {
const uri = reader.result as string;
resolve(uri.slice(uri.indexOf(',') + 1));
};
reader.onerror = reject;
});
}
@@ -0,0 +1,23 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export type {
TemplateFileAccess,
TemplateDirectoryAccess,
TemplateFileSystemAccess,
} from './types';
export { blobToBase64 } from './helpers';
export { WebFileSystemAccess } from './WebFileSystemAccess';
@@ -0,0 +1,29 @@
/*
* Copyright 2022 The Backstage Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export interface TemplateFileAccess {
path: string;
file(): Promise<File>;
save(data: string | BufferSource | Blob): Promise<void>;
}
export interface TemplateDirectoryAccess {
listFiles(): Promise<Array<TemplateFileAccess>>;
}
export interface TemplateFileSystemAccess {
requestDirectoryAccess(): Promise<TemplateDirectoryAccess>;
}