mirror of
https://github.com/withastro/astro.git
synced 2025-01-06 22:10:10 -05:00
feat: storage package
This commit is contained in:
parent
00420a7a52
commit
ef1230ae6d
9 changed files with 315 additions and 0 deletions
34
packages/storage/README.md
Normal file
34
packages/storage/README.md
Normal file
|
@ -0,0 +1,34 @@
|
|||
# @astrojs/studio
|
||||
|
||||
This package manages the connection between a local Astro project and [Astro Studio](studio). At this time, this package is not intended for direct use by end users, but rather as a dependency of other Astro packages.
|
||||
|
||||
## Support
|
||||
|
||||
- Get help in the [Astro Discord][discord]. Post questions in our `#support` forum, or visit our dedicated `#dev` channel to discuss current development and more!
|
||||
|
||||
- Check our [Astro Integration Documentation][astro-integration] for more on integrations.
|
||||
|
||||
- Submit bug reports and feature requests as [GitHub issues][issues].
|
||||
|
||||
## Contributing
|
||||
|
||||
This package is maintained by Astro's Core team. You're welcome to submit an issue or PR! These links will help you get started:
|
||||
|
||||
- [Contributor Manual][contributing]
|
||||
- [Code of Conduct][coc]
|
||||
- [Community Guide][community]
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
||||
Copyright (c) 2023–present [Astro][astro]
|
||||
|
||||
[astro]: https://astro.build/
|
||||
[contributing]: https://github.com/withastro/astro/blob/main/CONTRIBUTING.md
|
||||
[coc]: https://github.com/withastro/.github/blob/main/CODE_OF_CONDUCT.md
|
||||
[community]: https://github.com/withastro/.github/blob/main/COMMUNITY_GUIDE.md
|
||||
[discord]: https://astro.build/chat/
|
||||
[issues]: https://github.com/withastro/astro/issues
|
||||
[astro-integration]: https://docs.astro.build/en/guides/integrations-guide/
|
||||
[studio]: https://studio.astro.build/
|
48
packages/storage/package.json
Normal file
48
packages/storage/package.json
Normal file
|
@ -0,0 +1,48 @@
|
|||
{
|
||||
"name": "@astrojs/storage",
|
||||
"version": "0.1.0",
|
||||
"description": "Add libSQL and Astro Studio support to your Astro site",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/withastro/astro.git",
|
||||
"directory": "packages/storage"
|
||||
},
|
||||
"bugs": "https://github.com/withastro/astro/issues",
|
||||
"homepage": "https://docs.astro.build/en/guides/integrations-guide/studio/",
|
||||
"type": "module",
|
||||
"author": "withastro",
|
||||
"types": "./dist/index.js",
|
||||
"main": "./dist/index.js",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.js"
|
||||
},
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
],
|
||||
"keywords": [
|
||||
"withastro",
|
||||
"astro-integration"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "astro-scripts build \"src/**/*.ts\" && tsc",
|
||||
"build:ci": "astro-scripts build \"src/**/*.ts\"",
|
||||
"dev": "astro-scripts dev \"src/**/*.ts\""
|
||||
},
|
||||
"dependencies": {
|
||||
"ci-info": "^4.0.0",
|
||||
"kleur": "^4.1.5",
|
||||
"ora": "^8.0.1",
|
||||
"@astrojs/studio": "^0.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"astro": "workspace:*",
|
||||
"astro-scripts": "workspace:*",
|
||||
"typescript": "^5.4.5",
|
||||
"vite": "^5.2.11"
|
||||
}
|
||||
}
|
75
packages/storage/src/codegen.ts
Normal file
75
packages/storage/src/codegen.ts
Normal file
|
@ -0,0 +1,75 @@
|
|||
import type { AstroConfig } from 'astro';
|
||||
import { existsSync } from 'node:fs';
|
||||
import { mkdir, writeFile } from 'node:fs/promises';
|
||||
import { getAstroStudioStorageUrl } from './utils.js';
|
||||
import { STORAGE_CODE_FILE, STORAGE_TYPES_FILE } from './consts.js';
|
||||
import { getProjectIdFromFile, getSessionIdFromFile, safeFetch } from '@astrojs/studio';
|
||||
|
||||
export async function codegen(astroConfig: Pick<AstroConfig, 'root'>) {
|
||||
await codegenInternal({ root: astroConfig.root });
|
||||
}
|
||||
|
||||
async function codegenInternal({ root }: { root: URL }) {
|
||||
const dotAstroDir = new URL('.astro/', root);
|
||||
|
||||
const images = await storageRequest('image');
|
||||
const all = await storageRequest('all');
|
||||
|
||||
const code = `
|
||||
// This file is auto-generated by Astro Studio. Do not modify this file directly.
|
||||
export const images = ${JSON.stringify(images)};
|
||||
export const all = ${JSON.stringify(all)};
|
||||
|
||||
export type Image = keyof typeof images | string & {};
|
||||
export type File = keyof typeof all | string & {};
|
||||
`;
|
||||
|
||||
const types = `
|
||||
// This file is auto-generated by Astro Studio. Do not modify this file directly.
|
||||
declare module 'astro:storage' {
|
||||
export function getFile(name: import("./${STORAGE_CODE_FILE}").File);
|
||||
export function getStudioImage(name: import("./${STORAGE_CODE_FILE}").Image);
|
||||
}
|
||||
`
|
||||
|
||||
if (!existsSync(dotAstroDir)) {
|
||||
await mkdir(dotAstroDir);
|
||||
}
|
||||
|
||||
await writeFile(new URL(STORAGE_CODE_FILE, dotAstroDir), code);
|
||||
await writeFile(new URL(STORAGE_TYPES_FILE, dotAstroDir), types);
|
||||
}
|
||||
|
||||
async function storageRequest(fileKind: 'all' | 'image' = 'all') {
|
||||
const projectId = await getProjectIdFromFile();
|
||||
const linkUrl = getAstroStudioStorageUrl();
|
||||
const sessionToken = await getSessionIdFromFile();
|
||||
|
||||
const response = await safeFetch(
|
||||
linkUrl,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: {
|
||||
Authorization: `Bearer ${sessionToken}`,
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify({ projectId, fileKind }),
|
||||
},
|
||||
(res) => {
|
||||
// Unauthorized
|
||||
if (res.status === 401) {
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return groupDataByName((await response.json()).data);
|
||||
}
|
||||
|
||||
function groupDataByName(data: any) {
|
||||
const grouped: Record<string, any> = {};
|
||||
for (const item of data) {
|
||||
grouped[item.name] = item;
|
||||
}
|
||||
|
||||
return grouped;
|
||||
}
|
2
packages/storage/src/consts.ts
Normal file
2
packages/storage/src/consts.ts
Normal file
|
@ -0,0 +1,2 @@
|
|||
export const STORAGE_CODE_FILE = 'storage.ts';
|
||||
export const STORAGE_TYPES_FILE = 'storage-types.d.ts';
|
1
packages/storage/src/index.ts
Normal file
1
packages/storage/src/index.ts
Normal file
|
@ -0,0 +1 @@
|
|||
export { storageIntegration as default } from './integration.js'
|
115
packages/storage/src/integration.ts
Normal file
115
packages/storage/src/integration.ts
Normal file
|
@ -0,0 +1,115 @@
|
|||
import type { AstroConfig, AstroIntegration, AstroIntegrationLogger } from "astro";
|
||||
import { STORAGE_CODE_FILE, STORAGE_TYPES_FILE } from "./consts.js";
|
||||
import { codegen } from "./codegen.js";
|
||||
import { readFile, writeFile } from "node:fs/promises";
|
||||
import { bold } from "kleur/colors";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "url";
|
||||
import { normalizePath } from "vite";
|
||||
import { existsSync } from "node:fs";
|
||||
|
||||
const VIRTUAL_MODULE_ID = 'astro:storage';
|
||||
const RESOLVED_MODULE_ID = '\0' + 'astro:storage';
|
||||
|
||||
type VitePlugin = Required<AstroConfig['vite']>['plugins'][number];
|
||||
|
||||
function vitePluginStorage({ root }: { root: URL }): VitePlugin {
|
||||
const dotAstroDir = new URL('.astro/', root);
|
||||
|
||||
return {
|
||||
name: 'astro:storage',
|
||||
async resolveId(id) {
|
||||
if (id !== VIRTUAL_MODULE_ID) return;
|
||||
return RESOLVED_MODULE_ID;
|
||||
},
|
||||
async load(id) {
|
||||
if (id !== RESOLVED_MODULE_ID) return;
|
||||
|
||||
return `
|
||||
import { images, all } from '${new URL(STORAGE_CODE_FILE, dotAstroDir)}';
|
||||
|
||||
export function getFile(name) {
|
||||
return all[name];
|
||||
}
|
||||
|
||||
export function getStudioImage(name) {
|
||||
return images[name].id;
|
||||
}
|
||||
`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function storageIntegration(): AstroIntegration {
|
||||
return {
|
||||
name: "astro:studio",
|
||||
hooks: {
|
||||
'astro:config:setup': async ({ config, updateConfig, logger }) => {
|
||||
updateConfig({
|
||||
vite: {
|
||||
plugins: [vitePluginStorage({ root: config.root }), vitePluginInjectEnvTs({ srcDir: config.srcDir, root: config.root }, logger)]
|
||||
}
|
||||
})
|
||||
},
|
||||
'astro:config:done': async ({ config }) => {
|
||||
await codegen({ root: config.root });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function vitePluginInjectEnvTs(
|
||||
{ srcDir, root }: { srcDir: URL; root: URL },
|
||||
logger: AstroIntegrationLogger
|
||||
): VitePlugin {
|
||||
return {
|
||||
name: 'storage-inject-env-ts',
|
||||
enforce: 'post',
|
||||
async config() {
|
||||
await setUpEnvTs({ srcDir, root, logger });
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export async function setUpEnvTs({
|
||||
srcDir,
|
||||
root,
|
||||
logger,
|
||||
}: {
|
||||
srcDir: URL;
|
||||
root: URL;
|
||||
logger: AstroIntegrationLogger;
|
||||
}) {
|
||||
const envTsPath = getEnvTsPath({ srcDir });
|
||||
const envTsPathRelativetoRoot = normalizePath(
|
||||
path.relative(fileURLToPath(root), fileURLToPath(envTsPath))
|
||||
);
|
||||
|
||||
if (existsSync(envTsPath)) {
|
||||
let typesEnvContents = await readFile(envTsPath, 'utf-8');
|
||||
const dotAstroDir = new URL('.astro/', root);
|
||||
|
||||
if (!existsSync(dotAstroDir)) return;
|
||||
|
||||
const dbTypeReference = getStorageTypeReference({ srcDir, dotAstroDir });
|
||||
|
||||
if (!typesEnvContents.includes(dbTypeReference)) {
|
||||
typesEnvContents = `${dbTypeReference}\n${typesEnvContents}`;
|
||||
await writeFile(envTsPath, typesEnvContents, 'utf-8');
|
||||
logger.info(`Added ${bold(envTsPathRelativetoRoot)} types`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getStorageTypeReference({ srcDir, dotAstroDir }: { srcDir: URL; dotAstroDir: URL }) {
|
||||
const storageTypesFile = new URL(STORAGE_TYPES_FILE, dotAstroDir);
|
||||
const storageTypesRelativeToSrcDir = normalizePath(
|
||||
path.relative(fileURLToPath(srcDir), fileURLToPath(storageTypesFile))
|
||||
);
|
||||
|
||||
return `/// <reference path=${JSON.stringify(storageTypesRelativeToSrcDir)} />`;
|
||||
}
|
||||
|
||||
function getEnvTsPath({ srcDir }: { srcDir: URL }) {
|
||||
return new URL('env.d.ts', srcDir);
|
||||
}
|
5
packages/storage/src/utils.ts
Normal file
5
packages/storage/src/utils.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
import { getAstroStudioUrl } from "@astrojs/studio";
|
||||
|
||||
export function getAstroStudioStorageUrl(): string {
|
||||
return getAstroStudioUrl() + '/api/cli/files.list';
|
||||
}
|
7
packages/storage/tsconfig.json
Normal file
7
packages/storage/tsconfig.json
Normal file
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"extends": "../../tsconfig.base.json",
|
||||
"include": ["src"],
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist"
|
||||
}
|
||||
}
|
|
@ -5536,6 +5536,34 @@ importers:
|
|||
specifier: ^2.0.0
|
||||
version: 2.0.0
|
||||
|
||||
packages/storage:
|
||||
dependencies:
|
||||
'@astrojs/studio':
|
||||
specifier: ^0.1.0
|
||||
version: link:../studio
|
||||
ci-info:
|
||||
specifier: ^4.0.0
|
||||
version: 4.0.0
|
||||
kleur:
|
||||
specifier: ^4.1.5
|
||||
version: 4.1.5
|
||||
ora:
|
||||
specifier: ^8.0.1
|
||||
version: 8.0.1
|
||||
devDependencies:
|
||||
astro:
|
||||
specifier: workspace:*
|
||||
version: link:../astro
|
||||
astro-scripts:
|
||||
specifier: workspace:*
|
||||
version: link:../../scripts
|
||||
typescript:
|
||||
specifier: ^5.4.5
|
||||
version: 5.4.5
|
||||
vite:
|
||||
specifier: ^5.2.11
|
||||
version: 5.2.11(@types/node@18.19.31)(sass@1.77.1)
|
||||
|
||||
packages/studio:
|
||||
dependencies:
|
||||
ci-info:
|
||||
|
|
Loading…
Reference in a new issue