summaryrefslogtreecommitdiff
path: root/vendor
diff options
context:
space:
mode:
authorDawid Rycerz <dawid@rycerz.xyz>2025-07-21 21:56:55 +0300
committerDawid Rycerz <dawid@rycerz.xyz>2025-07-21 21:56:55 +0300
commitc735556726e75428550a3d28a2cf58e2c8490b7d (patch)
treefd0ae29d1636b825abeedff6b99d3376bb383135 /vendor
Initial template
Diffstat (limited to 'vendor')
-rw-r--r--vendor/README.md4
-rw-r--r--vendor/integration/index.ts116
-rw-r--r--vendor/integration/types.d.ts10
-rw-r--r--vendor/integration/utils/configBuilder.ts203
-rw-r--r--vendor/integration/utils/loadConfig.ts16
5 files changed, 349 insertions, 0 deletions
diff --git a/vendor/README.md b/vendor/README.md
new file mode 100644
index 0000000..8e14b86
--- /dev/null
+++ b/vendor/README.md
@@ -0,0 +1,4 @@
+This folder will become an integration for **AstroWind**.
+
+We are working to allow updates to template instances.
+These are changes on the way to new **AstroWind v2**
diff --git a/vendor/integration/index.ts b/vendor/integration/index.ts
new file mode 100644
index 0000000..9b7b726
--- /dev/null
+++ b/vendor/integration/index.ts
@@ -0,0 +1,116 @@
+import fs from 'node:fs';
+import os from 'node:os';
+import type { AstroConfig, AstroIntegration } from 'astro';
+
+import configBuilder, { type Config } from './utils/configBuilder';
+import loadConfig from './utils/loadConfig';
+
+export default ({ config: _themeConfig = 'src/config.yaml' } = {}): AstroIntegration => {
+ let cfg: AstroConfig;
+ return {
+ name: 'astrowind-integration',
+
+ hooks: {
+ 'astro:config:setup': async ({
+ // command,
+ config,
+ // injectRoute,
+ // isRestart,
+ logger,
+ updateConfig,
+ addWatchFile,
+ }) => {
+ const buildLogger = logger.fork('astrowind');
+
+ const virtualModuleId = 'astrowind:config';
+ const resolvedVirtualModuleId = '\0' + virtualModuleId;
+
+ const rawJsonConfig = (await loadConfig(_themeConfig)) as Config;
+ const { SITE, I18N, METADATA, APP_BLOG, UI, ANALYTICS } = configBuilder(rawJsonConfig);
+
+ updateConfig({
+ site: SITE.site,
+ base: SITE.base,
+
+ trailingSlash: SITE.trailingSlash ? 'always' : 'never',
+
+ vite: {
+ plugins: [
+ {
+ name: 'vite-plugin-astrowind-config',
+ resolveId(id) {
+ if (id === virtualModuleId) {
+ return resolvedVirtualModuleId;
+ }
+ },
+ load(id) {
+ if (id === resolvedVirtualModuleId) {
+ return `
+ export const SITE = ${JSON.stringify(SITE)};
+ export const I18N = ${JSON.stringify(I18N)};
+ export const METADATA = ${JSON.stringify(METADATA)};
+ export const APP_BLOG = ${JSON.stringify(APP_BLOG)};
+ export const UI = ${JSON.stringify(UI)};
+ export const ANALYTICS = ${JSON.stringify(ANALYTICS)};
+ `;
+ }
+ },
+ },
+ ],
+ },
+ });
+
+ if (typeof _themeConfig === 'string') {
+ addWatchFile(new URL(_themeConfig, config.root));
+
+ buildLogger.info(`Astrowind \`${_themeConfig}\` has been loaded.`);
+ } else {
+ buildLogger.info(`Astrowind config has been loaded.`);
+ }
+ },
+ 'astro:config:done': async ({ config }) => {
+ cfg = config;
+ },
+
+ 'astro:build:done': async ({ logger }) => {
+ const buildLogger = logger.fork('astrowind');
+ buildLogger.info('Updating `robots.txt` with `sitemap-index.xml` ...');
+
+ try {
+ const outDir = cfg.outDir;
+ const publicDir = cfg.publicDir;
+ const sitemapName = 'sitemap-index.xml';
+ const sitemapFile = new URL(sitemapName, outDir);
+ const robotsTxtFile = new URL('robots.txt', publicDir);
+ const robotsTxtFileInOut = new URL('robots.txt', outDir);
+
+ const hasIntegration =
+ Array.isArray(cfg?.integrations) &&
+ cfg.integrations?.find((e) => e?.name === '@astrojs/sitemap') !== undefined;
+ const sitemapExists = fs.existsSync(sitemapFile);
+
+ if (hasIntegration && sitemapExists) {
+ const robotsTxt = fs.readFileSync(robotsTxtFile, { encoding: 'utf8', flag: 'a+' });
+ const sitemapUrl = new URL(sitemapName, String(new URL(cfg.base, cfg.site)));
+ const pattern = /^Sitemap:(.*)$/m;
+
+ if (!pattern.test(robotsTxt)) {
+ fs.writeFileSync(robotsTxtFileInOut, `${robotsTxt}${os.EOL}${os.EOL}Sitemap: ${sitemapUrl}`, {
+ encoding: 'utf8',
+ flag: 'w',
+ });
+ } else {
+ fs.writeFileSync(robotsTxtFileInOut, robotsTxt.replace(pattern, `Sitemap: ${sitemapUrl}`), {
+ encoding: 'utf8',
+ flag: 'w',
+ });
+ }
+ }
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
+ } catch (error) {
+ /* empty */
+ }
+ },
+ },
+ };
+};
diff --git a/vendor/integration/types.d.ts b/vendor/integration/types.d.ts
new file mode 100644
index 0000000..3b6113b
--- /dev/null
+++ b/vendor/integration/types.d.ts
@@ -0,0 +1,10 @@
+declare module 'astrowind:config' {
+ import type { SiteConfig, I18NConfig, MetaDataConfig, AppBlogConfig, UIConfig, AnalyticsConfig } from './config';
+
+ export const SITE: SiteConfig;
+ export const I18N: I18NConfig;
+ export const METADATA: MetaDataConfig;
+ export const APP_BLOG: AppBlogConfig;
+ export const UI: UIConfig;
+ export const ANALYTICS: AnalyticsConfig;
+}
diff --git a/vendor/integration/utils/configBuilder.ts b/vendor/integration/utils/configBuilder.ts
new file mode 100644
index 0000000..1c60e9b
--- /dev/null
+++ b/vendor/integration/utils/configBuilder.ts
@@ -0,0 +1,203 @@
+import merge from 'lodash.merge';
+
+import type { MetaData } from '~/types';
+
+export type Config = {
+ site?: SiteConfig;
+ metadata?: MetaDataConfig;
+ i18n?: I18NConfig;
+ apps?: {
+ blog?: AppBlogConfig;
+ };
+ ui?: unknown;
+ analytics?: unknown;
+};
+
+export interface SiteConfig {
+ name: string;
+ site?: string;
+ base?: string;
+ trailingSlash?: boolean;
+ googleSiteVerificationId?: string;
+}
+export interface MetaDataConfig extends Omit<MetaData, 'title'> {
+ title?: {
+ default: string;
+ template: string;
+ };
+}
+export interface I18NConfig {
+ language: string;
+ textDirection: string;
+ dateFormatter?: Intl.DateTimeFormat;
+}
+export interface AppBlogConfig {
+ isEnabled: boolean;
+ postsPerPage: number;
+ isRelatedPostsEnabled: boolean;
+ relatedPostsCount: number;
+ post: {
+ isEnabled: boolean;
+ permalink: string;
+ robots: {
+ index: boolean;
+ follow: boolean;
+ };
+ };
+ list: {
+ isEnabled: boolean;
+ pathname: string;
+ robots: {
+ index: boolean;
+ follow: boolean;
+ };
+ };
+ category: {
+ isEnabled: boolean;
+ pathname: string;
+ robots: {
+ index: boolean;
+ follow: boolean;
+ };
+ };
+ tag: {
+ isEnabled: boolean;
+ pathname: string;
+ robots: {
+ index: boolean;
+ follow: boolean;
+ };
+ };
+}
+export interface AnalyticsConfig {
+ vendors: {
+ googleAnalytics: {
+ id?: string;
+ partytown?: boolean;
+ };
+ };
+}
+
+export interface UIConfig {
+ theme: string;
+}
+
+const DEFAULT_SITE_NAME = 'Website';
+
+const getSite = (config: Config) => {
+ const _default = {
+ name: DEFAULT_SITE_NAME,
+ site: undefined,
+ base: '/',
+ trailingSlash: false,
+
+ googleSiteVerificationId: '',
+ };
+
+ return merge({}, _default, config?.site ?? {}) as SiteConfig;
+};
+
+const getMetadata = (config: Config) => {
+ const siteConfig = getSite(config);
+
+ const _default = {
+ title: {
+ default: siteConfig?.name || DEFAULT_SITE_NAME,
+ template: '%s',
+ },
+ description: '',
+ robots: {
+ index: false,
+ follow: false,
+ },
+ openGraph: {
+ type: 'website',
+ },
+ };
+
+ return merge({}, _default, config?.metadata ?? {}) as MetaDataConfig;
+};
+
+const getI18N = (config: Config) => {
+ const _default = {
+ language: 'en',
+ textDirection: 'ltr',
+ };
+
+ const value = merge({}, _default, config?.i18n ?? {});
+
+ return value as I18NConfig;
+};
+
+const getAppBlog = (config: Config) => {
+ const _default = {
+ isEnabled: false,
+ postsPerPage: 6,
+ isRelatedPostsEnabled: false,
+ relatedPostsCount: 4,
+ post: {
+ isEnabled: true,
+ permalink: '/blog/%slug%',
+ robots: {
+ index: true,
+ follow: true,
+ },
+ },
+ list: {
+ isEnabled: true,
+ pathname: 'blog',
+ robots: {
+ index: true,
+ follow: true,
+ },
+ },
+ category: {
+ isEnabled: true,
+ pathname: 'category',
+ robots: {
+ index: true,
+ follow: true,
+ },
+ },
+ tag: {
+ isEnabled: true,
+ pathname: 'tag',
+ robots: {
+ index: false,
+ follow: true,
+ },
+ },
+ };
+
+ return merge({}, _default, config?.apps?.blog ?? {}) as AppBlogConfig;
+};
+
+const getUI = (config: Config) => {
+ const _default = {
+ theme: 'system',
+ };
+
+ return merge({}, _default, config?.ui ?? {});
+};
+
+const getAnalytics = (config: Config) => {
+ const _default = {
+ vendors: {
+ googleAnalytics: {
+ id: undefined,
+ partytown: true,
+ },
+ },
+ };
+
+ return merge({}, _default, config?.analytics ?? {}) as AnalyticsConfig;
+};
+
+export default (config: Config) => ({
+ SITE: getSite(config),
+ I18N: getI18N(config),
+ METADATA: getMetadata(config),
+ APP_BLOG: getAppBlog(config),
+ UI: getUI(config),
+ ANALYTICS: getAnalytics(config),
+});
diff --git a/vendor/integration/utils/loadConfig.ts b/vendor/integration/utils/loadConfig.ts
new file mode 100644
index 0000000..8dfb435
--- /dev/null
+++ b/vendor/integration/utils/loadConfig.ts
@@ -0,0 +1,16 @@
+import fs from 'node:fs';
+import yaml from 'js-yaml';
+
+const loadConfig = async (configPathOrData: string | object) => {
+ if (typeof configPathOrData === 'string') {
+ const content = fs.readFileSync(configPathOrData, 'utf8');
+ if (configPathOrData.endsWith('.yaml') || configPathOrData.endsWith('.yml')) {
+ return yaml.load(content);
+ }
+ return content;
+ }
+
+ return configPathOrData;
+};
+
+export default loadConfig;