61 | {#each viewData?.configuration.fields as field}
62 |
{record[field.name]}
63 | {/each}
64 |
65 | {/each}
66 | {/if}
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/src/server/api/getAppConfiguration.ts:
--------------------------------------------------------------------------------
1 | import { AppConfigurationType } from "../../types/schemas";
2 | import { loadAppConfiguration_ } from "../lib/loadAppConfiguration_";
3 |
4 | /**
5 | * **API Endpoint** | Returns the app configuration
6 | * @returns {AppConfiguration | null}
7 | */
8 | export function getAppConfiguration(): AppConfigurationType | null {
9 | console.log("getting app configuration");
10 |
11 | const appConfigurationObject = loadAppConfiguration_();
12 |
13 | console.log(appConfigurationObject);
14 |
15 | // Do we want to filter the appConfig based on user?
16 |
17 | return appConfigurationObject;
18 | }
19 |
--------------------------------------------------------------------------------
/src/server/api/getUser.ts:
--------------------------------------------------------------------------------
1 | import { UserType } from "../../types/schemas";
2 | import { createUser_ } from "../lib/createUser_";
3 | import { z } from "zod";
4 |
5 | export type GetUserArgs = {
6 | email: string | null;
7 | };
8 |
9 | /**
10 | * **API Endpoint** | Returns the accessing user object
11 | * @param {GetUserArgs} [optionalArgs] - Optional parameter containing user email. If no email is provided, the requesting user's email is used.
12 | * @returns {Promise}
13 | */
14 | async function getUser(
15 | { email }: GetUserArgs = { email: null }
16 | ): Promise {
17 | let requestingUserEmail = Session.getActiveUser().getEmail();
18 | // Report request
19 | console.log(
20 | "getUser called with args:",
21 | { email },
22 | " | by: ",
23 | requestingUserEmail
24 | );
25 |
26 | // Validate the arguments against the schema
27 | const GetUserArgsSchema = z.object({
28 | email: z.string().nullable(),
29 | });
30 | const validArgs = GetUserArgsSchema.parse({ email });
31 |
32 | let EMAIL_FOR_RETRIEVAL = validArgs.email || requestingUserEmail;
33 | let isRequestForSelf = requestingUserEmail === EMAIL_FOR_RETRIEVAL;
34 |
35 | const scriptPropertiesService = PropertiesService.getScriptProperties();
36 | const scriptProperties = scriptPropertiesService.getProperties();
37 | let userObjectString = scriptProperties[EMAIL_FOR_RETRIEVAL];
38 |
39 | // If the requested user's object doesnt exist and the request is from
40 | // someone other than the requested user, return null.
41 | if (!userObjectString && !isRequestForSelf) {
42 | return null;
43 | }
44 | // Else if the the request user's object doesn't exist but it is a request
45 | // from the requested user, create the user object and return it. They
46 | // now exist in the system.
47 | else if (!userObjectString && isRequestForSelf) {
48 | let user = createUser_(EMAIL_FOR_RETRIEVAL);
49 | return user;
50 | }
51 |
52 | // Otherwise, the user object exists and we can return it.
53 | let user = JSON.parse(userObjectString);
54 |
55 | return user;
56 | }
57 |
--------------------------------------------------------------------------------
/src/server/api/putAppConfiguration.ts:
--------------------------------------------------------------------------------
1 | import { AppConfiguration, AppConfigurationType } from "../../types/schemas";
2 |
3 | export type PutAppConfigArgs = {
4 | appConfiguration: AppConfigurationType;
5 | };
6 |
7 | /**
8 | * **API Endpoint** | Updates the app configuration and returns it
9 | * @param {PutAppConfigArgs} args
10 | * @returns {AppConfiguration | null}
11 | */
12 | function putAppConfiguration({
13 | appConfiguration,
14 | }: PutAppConfigArgs): AppConfigurationType {
15 | console.log("putAppConfiguration() called with: ", appConfiguration);
16 |
17 | const validAppConfiguration = AppConfiguration.parse(appConfiguration);
18 |
19 | const propertyKey = "appConfiguration";
20 | const scriptPropertiesService = PropertiesService.getScriptProperties();
21 |
22 | scriptPropertiesService.setProperty(
23 | propertyKey,
24 | JSON.stringify(appConfiguration)
25 | );
26 |
27 | return validAppConfiguration;
28 | }
29 |
--------------------------------------------------------------------------------
/src/server/api/putUser.ts:
--------------------------------------------------------------------------------
1 | import { User, UserType } from "../../types/schemas";
2 |
3 | export type PutUserArgs = {
4 | user: UserType;
5 | };
6 |
7 | /**
8 | * **API Endpoint** | Updates the app configuration and returns it
9 | * @param {PutUserArgs} args
10 | * @returns {UserType}
11 | */
12 | export function putUser({ user }: PutUserArgs): UserType {
13 | const invokingUserEmail = Session.getActiveUser().getEmail();
14 |
15 | console.log("putUser() called with: ", user, "by: ", invokingUserEmail);
16 |
17 | const validUser = User.parse(user);
18 |
19 | if (
20 | validUser.email !== invokingUserEmail &&
21 | validUser.email !== Session.getEffectiveUser().getEmail()
22 | ) {
23 | throw new Error(
24 | "A user resource can only be updated by themselves or the superAdmin."
25 | );
26 | }
27 |
28 | // If the code reaches here, the user object is valid
29 | // and the invoking user is either the user or a superAdmin.
30 | const propertyKey = validUser.email;
31 | const scriptPropertiesService = PropertiesService.getScriptProperties();
32 | scriptPropertiesService.setProperty(propertyKey, JSON.stringify(validUser));
33 |
34 | console.log("User successfully saved.");
35 | return validUser;
36 | }
37 |
--------------------------------------------------------------------------------
/src/server/doGet.js:
--------------------------------------------------------------------------------
1 | function doGet(e) {
2 | // We shouldn't load the application if we aren't able to get the user's
3 | // identity. In this case, we return the noAuth.html page.
4 | let activeUserEmail = Session.getActiveUser().getEmail();
5 | if (activeUserEmail === "") {
6 | return HtmlService.createTemplateFromFile("server/noAuth")
7 | .evaluate()
8 | .setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
9 | }
10 |
11 | // Otherwise, we check to see if this application has been initialized.
12 | // If not, we do so now
13 | let appConfig = loadAppConfiguration_();
14 | if (!appConfig) {
15 | initializeApp_();
16 | }
17 |
18 | // At this point we should
19 |
20 | return HtmlService.createHtmlOutputFromFile(
21 | "client/index.html"
22 | ).setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
23 | }
24 |
--------------------------------------------------------------------------------
/src/server/env.ts:
--------------------------------------------------------------------------------
1 | type EnvironmentDetails = {
2 | executeAs: "USER_DEPLOYING" | "USER_ACCESSING";
3 | domain: {
4 | type: "Personal" | "Workspace";
5 | name: string;
6 | };
7 | };
8 |
9 | export const ENV: EnvironmentDetails = {
10 | executeAs: "USER_DEPLOYING", // "USER_DEPLOYING" | "USER_ACCESSING"
11 | domain: {
12 | type: "Personal",
13 | name: "",
14 | },
15 | };
16 |
--------------------------------------------------------------------------------
/src/server/lib/createUser_.ts:
--------------------------------------------------------------------------------
1 | import { UserType, User } from "../../types/schemas";
2 | import { loadUserProfileImageUrl_ } from "./loadUserProfileImageUrl_";
3 |
4 | export function createUser_(email: string, overrides = {}): UserType {
5 | const scriptPropertiesService = PropertiesService.getScriptProperties();
6 | const profileImgUrl = loadUserProfileImageUrl_(email);
7 |
8 | let userDefaults: UserType = {
9 | email,
10 | roles: [],
11 | preferences: {
12 | theme: "light",
13 | },
14 | profile: {
15 | imageUrl: profileImgUrl,
16 | },
17 | activity: [
18 | {
19 | label: "User Created",
20 | value: new Date().toISOString(),
21 | },
22 | ],
23 | };
24 |
25 | let user = {
26 | ...userDefaults,
27 | ...overrides,
28 | };
29 |
30 | let validUser = User.parse(user); // throws if invalid
31 |
32 | scriptPropertiesService.setProperty(email, JSON.stringify(validUser));
33 |
34 | return user;
35 | }
36 |
--------------------------------------------------------------------------------
/src/server/lib/getAdmins_.ts:
--------------------------------------------------------------------------------
1 | import { UserType, User } from "../../types/schemas";
2 |
3 | export function getAdmins_(): UserType[] {
4 | // Return variable
5 | let adminUsers = [];
6 |
7 | // Load all of the script properties as an object
8 | const scriptProperties =
9 | PropertiesService.getScriptProperties().getProperties();
10 |
11 | // Loop through script properties object
12 | for (const property in scriptProperties) {
13 | try {
14 | // Weed out properties that do not reprsent users
15 | let user = User.parse(JSON.parse(scriptProperties[property]));
16 |
17 | // If the user has an admin role, add them to our list
18 | if (user.roles.includes("admin") || user.roles.includes("superAdmin")) {
19 | adminUsers.push(user);
20 | }
21 | } catch (error) {
22 | // Not a user, carry on
23 | }
24 | }
25 |
26 | return adminUsers;
27 | }
28 |
--------------------------------------------------------------------------------
/src/server/lib/getDeploymentDomain_.ts:
--------------------------------------------------------------------------------
1 | /**
2 | *
3 | * @returns
4 | */
5 | export function getDeploymentDomain_() {
6 | try {
7 | // @ts-ignore
8 | // We don't have People Advanced Service types
9 | let people = People.People.searchDirectoryPeople({
10 | query: "",
11 | readMask: "photos",
12 | sources: "DIRECTORY_SOURCE_TYPE_DOMAIN_PROFILE",
13 | });
14 | } catch (err) {
15 | if (err.toString().includes("Must be a G Suite domain user")) {
16 | return "Personal";
17 | } else {
18 | console.error(err);
19 | }
20 | }
21 |
22 | return "Business";
23 | }
24 |
--------------------------------------------------------------------------------
/src/server/lib/initializeApp_.ts:
--------------------------------------------------------------------------------
1 | import { AppConfigurationType } from "../../types/schemas";
2 | import { createUser_ } from "./createUser_";
3 | import { loadAppConfiguration_ } from "./loadAppConfiguration_";
4 | import { ENV } from "../env";
5 |
6 | /**
7 | * Initialize the app
8 | */
9 | export function initializeApp_(): Object {
10 | let superAdminEmail: string = "";
11 | if (ENV.executeAs === "USER_DEPLOYING") {
12 | superAdminEmail = Session.getEffectiveUser().getEmail();
13 | } else if (ENV.executeAs === "USER_ACCESSING") {
14 | superAdminEmail = DriveApp.getFileById(ScriptApp.getScriptId())
15 | .getOwner()
16 | .getEmail();
17 | } else {
18 | throw `App could not be initialized`;
19 | }
20 |
21 | const deployingUserEmail = Session.getEffectiveUser().getEmail();
22 |
23 | createUser_(superAdminEmail, { roles: ["superAdmin"] });
24 |
25 | let newAppConfig: AppConfigurationType = {
26 | appName: "My App",
27 | deployingUserEmail: deployingUserEmail,
28 | admins: [],
29 | };
30 |
31 | const scriptPropertiesService = PropertiesService.getScriptProperties();
32 | scriptPropertiesService.setProperty(
33 | "appConfiguration",
34 | JSON.stringify(newAppConfig)
35 | );
36 |
37 | let appConfig = loadAppConfiguration_();
38 |
39 | return JSON.parse(JSON.stringify(appConfig));
40 | }
41 |
--------------------------------------------------------------------------------
/src/server/lib/loadAppConfiguration_.ts:
--------------------------------------------------------------------------------
1 | import { AppConfigurationType, AppConfiguration } from "../../types/schemas";
2 |
3 | /**
4 | * Loads the app configuration from the script properties
5 | * @returns {AppConfiguration | null}
6 | */
7 | const loadAppConfiguration_ = (): AppConfigurationType | null => {
8 | const scriptPropertiesService = PropertiesService.getScriptProperties();
9 | const scriptProperties = scriptPropertiesService.getProperties();
10 | const appConfigurationString = scriptProperties.appConfiguration || null;
11 |
12 | if (!appConfigurationString) {
13 | return null;
14 | }
15 |
16 | let appConfig: AppConfigurationType = {
17 | ...JSON.parse(appConfigurationString),
18 | admins: getAdmins_(),
19 | };
20 |
21 | AppConfiguration.parse(appConfig);
22 |
23 | return appConfig;
24 | }
25 |
26 | export {
27 | loadAppConfiguration_,
28 | }
--------------------------------------------------------------------------------
/src/server/lib/loadUserProfileImageUrl_.ts:
--------------------------------------------------------------------------------
1 | import { ENV } from "../env";
2 |
3 | /**
4 | *
5 | * @param {string} email
6 | * @returns {string} A promise resolving to the user's profile image URL or a default img URL.
7 | */
8 | export function loadUserProfileImageUrl_(email: string): string {
9 | let userPictureUrl;
10 | let defaultPictureUrl =
11 | "https://lh3.googleusercontent.com/a-/AOh14Gj-cdUSUVoEge7rD5a063tQkyTDT3mripEuDZ0v=s100";
12 | try {
13 | // @ts-ignore
14 | // We don't have People Advanced Service types
15 | let people = People.People.searchDirectoryPeople({
16 | query: email,
17 | readMask: "photos",
18 | sources: "DIRECTORY_SOURCE_TYPE_DOMAIN_PROFILE",
19 | });
20 |
21 | userPictureUrl = people.people[0].photos[0].url;
22 | } catch (err) {
23 | console.log(err);
24 | }
25 |
26 | return userPictureUrl ?? defaultPictureUrl;
27 | }
28 |
29 | // function loadUserProfileImageUrl_(email: string | null = null): string {
30 |
31 | // let defaultPictureUrl = "https://lh3.googleusercontent.com/a-/AOh14Gj-cdUSUVoEge7rD5a063tQkyTDT3mripEuDZ0v=s100";
32 | // let profileImageUrl = defaultPictureUrl
33 |
34 | // if (email === null) {
35 |
36 | // }
37 |
38 | // if (ENV.executeAs === "USER_ACCESSING") {
39 | // profileImageUrl = DriveApp.getRootFolder().getOwner().getPhotoUrl();
40 | // }
41 |
42 | // return profileImageUrl
43 | // }
44 |
--------------------------------------------------------------------------------
/src/server/lib/query.js:
--------------------------------------------------------------------------------
1 | function convertToObjects_(data) {
2 | const [headers, ...rows] = data;
3 | return rows.map((row) => {
4 | return headers.reduce((acc, header, index) => {
5 | acc[header] = row[index];
6 | return acc;
7 | }, {});
8 | });
9 | }
10 |
11 | /**
12 | * @param {ViewConfiguration} viewConfiguration
13 | * @returns {QueryResult}
14 | */
15 | function query(viewConfiguration) {
16 | const { spreadsheetId, gid } = viewConfiguration.dataSource;
17 | const { fields } = viewConfiguration;
18 |
19 | const ss = SpreadsheetApp.openById(spreadsheetId);
20 | const sheet = getSheetById(ss, parseInt(gid));
21 | const dataRange = sheet.getDataRange();
22 | const values = dataRange.getValues();
23 | const records = convertToObjects_(values);
24 |
25 | const data = records.map((record) => {
26 | return fields.reduce((acc, column) => {
27 | acc[column.name] = record[column.name];
28 | return acc;
29 | }, {});
30 | });
31 |
32 | return {
33 | producedAt: new Date().toISOString(),
34 | records: data,
35 | };
36 | }
37 |
--------------------------------------------------------------------------------
/src/server/noAuth.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | This application requires your Google account to be identifiable in order
10 | to work properly. The link you're accessing now has been deployed in such
11 | a way that prevents the app from accessing your account. Please contact
12 | = Session.getEffectiveUser().getEmail() ?>
13 | for assistance.
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/src/stores.ts:
--------------------------------------------------------------------------------
1 | import { derived, writable } from "svelte/store";
2 | import { UserType } from "./types/schemas";
3 | import { AppConfigurationType } from "./types/schemas";
4 |
5 | export const sessionUser = writable(null);
6 | export const userIsAdmin = derived(sessionUser, ($sessionUser) => {
7 | return (
8 | $sessionUser?.roles.includes("admin") ||
9 | $sessionUser?.roles.includes("superAdmin")
10 | );
11 | });
12 |
13 | export const isLoading = writable(false);
14 |
15 | export const appConfiguration = writable(null);
16 |
--------------------------------------------------------------------------------
/src/types/schemas.ts:
--------------------------------------------------------------------------------
1 | import { z } from "zod";
2 |
3 | // const UserPreferences = z.record(z.any());
4 | const UserPreferences = z.object({
5 | theme: z.enum(["light", "dark"]).optional(),
6 | });
7 |
8 | const UserProfile = z
9 | .object({
10 | imageUrl: z.string(),
11 | })
12 | .and(z.record(z.string()))
13 | .optional();
14 |
15 | const UserActivity = z.object({
16 | label: z.string(),
17 | value: z.string(), // You can add custom validation to ensure it's an ISO string
18 | });
19 |
20 | const UserRoles = z.array(z.enum(["superAdmin", "admin"]));
21 |
22 | const User = z.object({
23 | email: z.string().email(),
24 | roles: UserRoles,
25 | profile: UserProfile,
26 | preferences: UserPreferences,
27 | activity: z.array(UserActivity),
28 | });
29 |
30 | const AppConfiguration = z.object({
31 | appName: z.string(),
32 | deployingUserEmail: z.string(),
33 | admins: z.array(User),
34 | });
35 |
36 | // You need to export in this format. See
37 | // https://stackoverflow.com/questions/48791868/use-typescript-with-google-apps-script
38 | // for more info.
39 | export { AppConfiguration, UserPreferences, UserProfile, UserActivity, User };
40 |
41 | export type AppConfigurationType = z.infer;
42 | export type UserPreferencesType = z.infer;
43 | export type UserProfileType = z.infer;
44 | export type UserActivityType = z.infer;
45 | export type UserType = z.infer;
46 |
--------------------------------------------------------------------------------
/src/vite-env.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
--------------------------------------------------------------------------------
/svelte.config.js:
--------------------------------------------------------------------------------
1 | import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'
2 |
3 | export default {
4 | // Consult https://svelte.dev/docs#compile-time-svelte-preprocess
5 | // for more information about preprocessors
6 | preprocess: vitePreprocess(),
7 | }
8 |
--------------------------------------------------------------------------------
/tailwind.config.cjs:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | content: ['./src/**/*.{html,js,svelte,ts}'],
3 | theme: {
4 | extend: {}
5 | },
6 | plugins: [require("daisyui")],
7 |
8 | daisyui: {
9 | themes: ["light", "dark"],
10 | },
11 | };
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | /* Visit https://aka.ms/tsconfig to read more about this file */
4 |
5 | /* Projects */
6 | // "incremental": true, /* Save .tsbuildinfo files to allow for incremental compilation of projects. */
7 | // "composite": true, /* Enable constraints that allow a TypeScript project to be used with project references. */
8 | // "tsBuildInfoFile": "./.tsbuildinfo", /* Specify the path to .tsbuildinfo incremental compilation file. */
9 | // "disableSourceOfProjectReferenceRedirect": true, /* Disable preferring source files instead of declaration files when referencing composite projects. */
10 | // "disableSolutionSearching": true, /* Opt a project out of multi-project reference checking when editing. */
11 | // "disableReferencedProjectLoad": true, /* Reduce the number of projects loaded automatically by TypeScript. */
12 |
13 | /* Language and Environment */
14 | "target": "es2016", /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
15 | // "lib": [], /* Specify a set of bundled library declaration files that describe the target runtime environment. */
16 | // "jsx": "preserve", /* Specify what JSX code is generated. */
17 | // "experimentalDecorators": true, /* Enable experimental support for legacy experimental decorators. */
18 | // "emitDecoratorMetadata": true, /* Emit design-type metadata for decorated declarations in source files. */
19 | // "jsxFactory": "", /* Specify the JSX factory function used when targeting React JSX emit, e.g. 'React.createElement' or 'h'. */
20 | // "jsxFragmentFactory": "", /* Specify the JSX Fragment reference used for fragments when targeting React JSX emit e.g. 'React.Fragment' or 'Fragment'. */
21 | // "jsxImportSource": "", /* Specify module specifier used to import the JSX factory functions when using 'jsx: react-jsx*'. */
22 | // "reactNamespace": "", /* Specify the object invoked for 'createElement'. This only applies when targeting 'react' JSX emit. */
23 | // "noLib": true, /* Disable including any library files, including the default lib.d.ts. */
24 | // "useDefineForClassFields": true, /* Emit ECMAScript-standard-compliant class fields. */
25 | // "moduleDetection": "auto", /* Control what method is used to detect module-format JS files. */
26 |
27 | /* Modules */
28 | "module": "commonjs", /* Specify what module code is generated. */
29 | // "rootDir": "./", /* Specify the root folder within your source files. */
30 | // "moduleResolution": "node10", /* Specify how TypeScript looks up a file from a given module specifier. */
31 | // "baseUrl": "./", /* Specify the base directory to resolve non-relative module names. */
32 | // "paths": {}, /* Specify a set of entries that re-map imports to additional lookup locations. */
33 | // "rootDirs": [], /* Allow multiple folders to be treated as one when resolving modules. */
34 | // "typeRoots": [], /* Specify multiple folders that act like './node_modules/@types'. */
35 | // "types": [], /* Specify type package names to be included without being referenced in a source file. */
36 | // "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
37 | // "moduleSuffixes": [], /* List of file name suffixes to search when resolving a module. */
38 | // "allowImportingTsExtensions": true, /* Allow imports to include TypeScript file extensions. Requires '--moduleResolution bundler' and either '--noEmit' or '--emitDeclarationOnly' to be set. */
39 | // "resolvePackageJsonExports": true, /* Use the package.json 'exports' field when resolving package imports. */
40 | // "resolvePackageJsonImports": true, /* Use the package.json 'imports' field when resolving imports. */
41 | // "customConditions": [], /* Conditions to set in addition to the resolver-specific defaults when resolving imports. */
42 | // "resolveJsonModule": true, /* Enable importing .json files. */
43 | // "allowArbitraryExtensions": true, /* Enable importing files with any extension, provided a declaration file is present. */
44 | // "noResolve": true, /* Disallow 'import's, 'require's or ''s from expanding the number of files TypeScript should add to a project. */
45 |
46 | /* JavaScript Support */
47 | // "allowJs": true, /* Allow JavaScript files to be a part of your program. Use the 'checkJS' option to get errors from these files. */
48 | // "checkJs": true, /* Enable error reporting in type-checked JavaScript files. */
49 | // "maxNodeModuleJsDepth": 1, /* Specify the maximum folder depth used for checking JavaScript files from 'node_modules'. Only applicable with 'allowJs'. */
50 |
51 | /* Emit */
52 | // "declaration": true, /* Generate .d.ts files from TypeScript and JavaScript files in your project. */
53 | // "declarationMap": true, /* Create sourcemaps for d.ts files. */
54 | // "emitDeclarationOnly": true, /* Only output d.ts files and not JavaScript files. */
55 | // "sourceMap": true, /* Create source map files for emitted JavaScript files. */
56 | // "inlineSourceMap": true, /* Include sourcemap files inside the emitted JavaScript. */
57 | // "outFile": "./", /* Specify a file that bundles all outputs into one JavaScript file. If 'declaration' is true, also designates a file that bundles all .d.ts output. */
58 | // "outDir": "./", /* Specify an output folder for all emitted files. */
59 | // "removeComments": true, /* Disable emitting comments. */
60 | // "noEmit": true, /* Disable emitting files from a compilation. */
61 | // "importHelpers": true, /* Allow importing helper functions from tslib once per project, instead of including them per-file. */
62 | // "importsNotUsedAsValues": "remove", /* Specify emit/checking behavior for imports that are only used for types. */
63 | // "downlevelIteration": true, /* Emit more compliant, but verbose and less performant JavaScript for iteration. */
64 | // "sourceRoot": "", /* Specify the root path for debuggers to find the reference source code. */
65 | // "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
66 | // "inlineSources": true, /* Include source code in the sourcemaps inside the emitted JavaScript. */
67 | // "emitBOM": true, /* Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files. */
68 | // "newLine": "crlf", /* Set the newline character for emitting files. */
69 | // "stripInternal": true, /* Disable emitting declarations that have '@internal' in their JSDoc comments. */
70 | // "noEmitHelpers": true, /* Disable generating custom helper functions like '__extends' in compiled output. */
71 | // "noEmitOnError": true, /* Disable emitting files if any type checking errors are reported. */
72 | // "preserveConstEnums": true, /* Disable erasing 'const enum' declarations in generated code. */
73 | // "declarationDir": "./", /* Specify the output directory for generated declaration files. */
74 | // "preserveValueImports": true, /* Preserve unused imported values in the JavaScript output that would otherwise be removed. */
75 |
76 | /* Interop Constraints */
77 | // "isolatedModules": true, /* Ensure that each file can be safely transpiled without relying on other imports. */
78 | // "verbatimModuleSyntax": true, /* Do not transform or elide any imports or exports not marked as type-only, ensuring they are written in the output file's format based on the 'module' setting. */
79 | // "allowSyntheticDefaultImports": true, /* Allow 'import x from y' when a module doesn't have a default export. */
80 | "esModuleInterop": true, /* Emit additional JavaScript to ease support for importing CommonJS modules. This enables 'allowSyntheticDefaultImports' for type compatibility. */
81 | // "preserveSymlinks": true, /* Disable resolving symlinks to their realpath. This correlates to the same flag in node. */
82 | "forceConsistentCasingInFileNames": true, /* Ensure that casing is correct in imports. */
83 |
84 | /* Type Checking */
85 | "strict": true, /* Enable all strict type-checking options. */
86 | // "noImplicitAny": true, /* Enable error reporting for expressions and declarations with an implied 'any' type. */
87 | // "strictNullChecks": true, /* When type checking, take into account 'null' and 'undefined'. */
88 | // "strictFunctionTypes": true, /* When assigning functions, check to ensure parameters and the return values are subtype-compatible. */
89 | // "strictBindCallApply": true, /* Check that the arguments for 'bind', 'call', and 'apply' methods match the original function. */
90 | // "strictPropertyInitialization": true, /* Check for class properties that are declared but not set in the constructor. */
91 | // "noImplicitThis": true, /* Enable error reporting when 'this' is given the type 'any'. */
92 | // "useUnknownInCatchVariables": true, /* Default catch clause variables as 'unknown' instead of 'any'. */
93 | // "alwaysStrict": true, /* Ensure 'use strict' is always emitted. */
94 | // "noUnusedLocals": true, /* Enable error reporting when local variables aren't read. */
95 | // "noUnusedParameters": true, /* Raise an error when a function parameter isn't read. */
96 | // "exactOptionalPropertyTypes": true, /* Interpret optional property types as written, rather than adding 'undefined'. */
97 | // "noImplicitReturns": true, /* Enable error reporting for codepaths that do not explicitly return in a function. */
98 | // "noFallthroughCasesInSwitch": true, /* Enable error reporting for fallthrough cases in switch statements. */
99 | // "noUncheckedIndexedAccess": true, /* Add 'undefined' to a type when accessed using an index. */
100 | // "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an override modifier. */
101 | // "noPropertyAccessFromIndexSignature": true, /* Enforces using indexed accessors for keys declared using an indexed type. */
102 | // "allowUnusedLabels": true, /* Disable error reporting for unused labels. */
103 | // "allowUnreachableCode": true, /* Disable error reporting for unreachable code. */
104 |
105 | /* Completeness */
106 | // "skipDefaultLibCheck": true, /* Skip type checking .d.ts files that are included with TypeScript. */
107 | "skipLibCheck": true /* Skip type checking all .d.ts files. */
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import { viteSingleFile } from 'vite-plugin-singlefile';
3 | import { svelte } from '@sveltejs/vite-plugin-svelte'
4 | import copy from 'rollup-plugin-copy';
5 | import del from 'rollup-plugin-delete';
6 | import { resolve } from 'path';
7 |
8 | // https://vitejs.dev/config/
9 | export default defineConfig({
10 | plugins: [
11 | svelte(),
12 | viteSingleFile(),
13 |
14 | // Delete the dist/ directory before each build
15 | del({ targets: 'dist/*' }),
16 | copy({
17 | targets: [
18 | { src: 'src/appsscript.json', dest: 'dist' },
19 | ]
20 | }),
21 | copy({
22 | targets: [
23 | { src: 'src/types', dest: 'dist' },
24 | { src: 'src/server', dest: 'dist' }
25 | ],
26 | flatten: false,
27 | }),
28 | ],
29 | build: {
30 | minify: true,
31 | outDir: resolve(__dirname, 'dist/client'),
32 | },
33 | })
34 |
--------------------------------------------------------------------------------