diff --git a/src/env.d.ts b/src/env.d.ts
index c47f586..b0185e3 100644
--- a/src/env.d.ts
+++ b/src/env.d.ts
@@ -2,3 +2,4 @@
///
///
///
+///
diff --git a/src/integration/README.md b/src/integration/README.md
new file mode 100644
index 0000000..b3fd285
--- /dev/null
+++ b/src/integration/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**
\ No newline at end of file
diff --git a/src/integration/index.mjs b/src/integration/index.mjs
new file mode 100644
index 0000000..3a12e46
--- /dev/null
+++ b/src/integration/index.mjs
@@ -0,0 +1,115 @@
+import fs from 'node:fs';
+import os from 'node:os';
+
+import yaml from 'js-yaml';
+
+import configBuilder from "./utils/configBuilder"
+
+const tasksIntegration = () => {
+ let config;
+ return {
+ name: 'AstroWind:tasks',
+
+ 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 fileConfig = yaml.load(fs.readFileSync('src/config.yaml', 'utf8'));
+ const { SITE, I18N, METADATA, APP_BLOG, UI, ANALYTICS } = configBuilder(fileConfig);
+
+ 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)};
+ `;
+ }
+ },
+ },
+ ],
+ },
+ });
+
+ addWatchFile(new URL('./src/config.yaml', config.root));
+
+ buildLogger.info("Astrowind `src/config.yaml` has been loaded.")
+ },
+ 'astro:config:done': async ({ config: cfg }) => {
+ config = cfg;
+ },
+
+ 'astro:build:done': async ({ logger }) => {
+
+ const buildLogger = logger.fork("astrowind");
+ buildLogger.info("Updating `robots.txt` with `sitemap-index.xml` ...")
+
+ try {
+ const outDir = config.outDir;
+ const publicDir = config.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(config?.integrations) &&
+ config.integrations?.find((e) => e?.name === '@astrojs/sitemap') !== undefined;
+ const sitemapExists = fs.existsSync(sitemapFile);
+
+ if (hasIntegration && sitemapExists) {
+ const robotsTxt = fs.readFileSync(robotsTxtFile, { encoding: 'utf8', flags: 'a+' });
+ const sitemapUrl = new URL(sitemapName, String(new URL(config.base, config.site)));
+ const pattern = /^Sitemap:(.*)$/m;
+
+ if (!pattern.test(robotsTxt)) {
+ fs.appendFileSync(robotsTxtFileInOut, `${os.EOL}${os.EOL}Sitemap: ${sitemapUrl}`, {
+ encoding: 'utf8',
+ flags: 'w',
+ });
+ } else {
+ fs.writeFileSync(robotsTxtFileInOut, robotsTxt.replace(pattern, `Sitemap: ${sitemapUrl}`), {
+ encoding: 'utf8',
+ flags: 'w',
+ });
+ }
+ }
+ } catch (err) {
+ /* empty */
+ }
+ },
+ },
+ };
+};
+
+export default tasksIntegration;
diff --git a/src/integration/types.d.ts b/src/integration/types.d.ts
new file mode 100644
index 0000000..a8cac31
--- /dev/null
+++ b/src/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;
+}
\ No newline at end of file
diff --git a/src/integration/utils/configBuilder.ts b/src/integration/utils/configBuilder.ts
new file mode 100644
index 0000000..18df411
--- /dev/null
+++ b/src/integration/utils/configBuilder.ts
@@ -0,0 +1,228 @@
+import merge from 'lodash.merge';
+
+import type { MetaData } from '~/types';
+
+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 {
+ 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 {}
+
+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',
+ classes: {},
+ tokens: {
+ default: {
+ fonts: {},
+ colors: {
+ default: 'rgb(16 16 16)',
+ heading: 'rgb(0 0 0)',
+ muted: 'rgb(16 16 16 / 66%)',
+ bgPage: 'rgb(255 255 255)',
+ primary: 'rgb(1 97 239)',
+ secondary: 'rgb(1 84 207)',
+ accent: 'rgb(109 40 217)',
+ },
+ },
+ dark: {
+ fonts: {},
+ colors: {
+ default: 'rgb(229 236 246)',
+ heading: 'rgb(247, 248, 248)',
+ muted: 'rgb(229 236 246 / 66%)',
+ bgPage: 'rgb(3 6 32)',
+ primary: 'rgb(1 97 239)',
+ secondary: 'rgb(1 84 207)',
+ accent: 'rgb(109 40 217)',
+ },
+ },
+ },
+ };
+
+ 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),
+});