import { default as ts } from 'typescript'; import { Plugin } from 'vite'; import { build as esbuildBuild } from 'esbuild'; import { CompilerContext } from './context/index.ts'; import { WebTypesOptions } from './docs/webTypes/types.ts'; import { VsCodeDocOptions } from './docs/vsCodeCustomData/types.ts'; import { DependencyManagementOptions as BaseDependencyManagementOptions } from '@arcgis/components-build-utils'; import { ApiExtractorPluginConfig } from '@arcgis/api-extractor/vite/plugin'; import { ResolvedApiExtractorConfig } from '@arcgis/api-extractor/extractor/config'; import { CopyDocDefinitions } from '@arcgis/api-extractor/extractor/extractors/copyDoc'; import { ApiJson, ApiModule } from '@arcgis/api-extractor/apiJson'; /** * `useLumina()` returns an array of Vite plugins. The array additionally * has a `context` property for accessing the compiler context. */ export type LuminaPlugins = Plugin[] & { readonly context: CompilerContext; }; /** * Options for controlling the Lumina Compiler. */ export type LuminaOptions = { /** * Configuration for controlling the build output, dependency externalization * and CDN build process. */ readonly build?: BuildOptions; /** * Configurations for controlling the types generation. */ readonly types?: GenerateTypesOptions; /** * Configurations for controlling the serve and test environment. */ readonly serve?: ServeEnvironmentOptions; /** * Configuration for Puppeteer End-to-End tests. * For more options, see Vite's "testing" option. * This is equivalent to the `e2e` mode in Stencil. * * @deprecated * For new projects, use Vitest browser mode instead of Puppeteer. * See documentation: https://webgis.esri.com/references/lumina/testing */ readonly puppeteerTesting?: PuppeteerTestingOptions; /** * Configurations for controlling the generation of documentation. */ readonly documentation?: GenerateDocumentationOptions; readonly apiExtractor?: ApiExtractorPluginConfig; /** * Configurations for controlling the extraction of components metadata and * generation of extra files such as types, docs, and wrappers. * * This is for advanced users who want to customize the generation of metadata * files. */ readonly apiJson?: ApiJsonOptions; /** * Configuration for CSS handling. */ readonly css?: CssHandlingOptions; /** * Configuration for assets handling. */ readonly assets?: AssetHandlingOptions; readonly linting?: LintingOptions; readonly experimental?: { /** @deprecated See https://discord.com/channels/1012791295170859069/1047015641225371718 */ readonly stabilizeRef?: boolean; }; }; export type BuildOptions = { /** * Configuration for CDN build. */ readonly cdn?: BuildCdnOptions; /** @deprecated Use Storybook build or separate test app package. */ readonly webApp?: { /** * If set, will produce a web app build, based on index.html files and * cdn build. * * @default undefined * @example "www" * @see [Based on Stencil's www build](https://stenciljs.com/docs/www) * @deprecated Use Storybook build or a separate test app package. */ readonly destination?: string; /** @deprecated Use Storybook build or a separate test app package. */ readonly transformFile?: (htmlContent: string, htmlFilePath: string, context: CompilerContext) => string; /** @deprecated Use Storybook build or a separate test app package. */ readonly afterBuildEnd?: (context: CompilerContext) => Promise | void; }; /** * Options for externalizing or bundling in dependencies. */ readonly dependencies?: DependencyManagementOptions; /** * Legal comment to add to the top of each entrypoint. * New lines are accepted. * Do not include // or /* - will be added automatically. * * @default require("@arcgis/toolkit/string").getPreamble() * * @example * ```js * `COPYRIGHT Esri - https://js.arcgis.com/{minorVersion}/LICENSE.txt` * ``` */ readonly preamble?: string; /** * Whether to include source maps even in production builds. * Only set this to true if your library is open source. * * @default false */ readonly enableSourceMapsInProduction?: boolean; /** * Wrappers to generate. * @deprecated Wrappers will be dropped in 6.0 */ readonly wrappers?: WrappersOptions[]; }; export type BuildCdnOptions = { /** * Whether to skip CDN build. * * @default false * * @remarks * Even if CDN build is skipped, assets will still be emitted to * dist/cdn-folder-name/assets */ readonly skip?: boolean; /** * @deprecated To improve CDN experience and simplify redirect handling, * deprecated namespace to use consistent CDN folder and file names. * * Reference: https://devtopia.esri.com/WebGIS/arcgis-web-components/issues/5051#issuecomment-5482327 * * Determines the CDN folder name and the name of the entrypoint file inside. * * @default "cdn" */ readonly namespace?: string; /** * To produce the CDN build, Lumina takes the dist/loader.js file and * and post-processes it with ESBuild to make it ready for consumption in the * browser. * * You can customize the options passed to ESBuild. Note, some options may * conflict with the Lumina build process. * * @see https://esbuild.github.io/api/#overview */ readonly esbuildOptions?: Parameters[0]; /** * Transform the CDN entrypoint before it is processed by ESBuild. * * This callback is for advanced use cases only. The build output may change * between Lumina versions without prior notice. */ readonly entrypointTransformer?: (code: string, context: CompilerContext) => string; /** * A callback for mutating CDN ESBuild output chunks before they are written * to disk. * This callback is for advanced use cases only. The build output may change * between Lumina versions without prior notice. Using this callback may * corrupt the CDN source map if it is enabled. */ readonly transformer?: (chunks: Map, context: CompilerContext) => void; }; export type CdnChunk = { code: string; /** * Whether chunk is currently being processed. Used to detect cyclical imports. * @private */ isProcessing: boolean; isAsync: boolean | undefined; oldSingleExportName: string | undefined; }; /** * Generic type for the different kinds of wrappers. * * Angular wrappers have been deprecated. See: * https://devtopia.esri.com/WebGIS/arcgis-web-components/discussions/1281 * * @deprecated Wrappers will be dropped in 6.0 */ export type WrappersOptions = React18WrapperOptions; /** * Options for creating React 18 wrapper. * * Documentation: https://webgis.esri.com/references/lumina/build#build-wrappers * * @deprecated Wrappers will be dropped in 6.0 */ export type React18WrapperOptions = { /** * @deprecated Wrappers will be dropped in 6.0 */ readonly type: "react18"; /** * The file path to the React 18 wrapper proxies file. * @example * "../lumina-components-react/src/components.ts" */ readonly proxiesFile: string; }; /** * Options for externalizing dependencies. * * @remarks * If Vite is running under Storybook, these options are ignored. This is * because in Storybook build, unlike in a library build, we want to bundle in * all dependencies (with exception of @arcgis/core and web component * dependencies, which are loaded using the ESM CDN). */ export type DependencyManagementOptions = BaseDependencyManagementOptions & { /** * By default, Lumina will error if it encounters usage of an unknown custom * element. This setting allows to silence such error if you are manually * importing some custom elements. * * > [!IMPORTANT] * > * > This option is an escape hatch. Build-time optimizations are not applied * > to such custom elements. Additionally, you assume the responsibility for * > correctly loading the web component in lazy, non-lazy and CDN builds. * * @example * ```ts * // Silence errors for unknown custom elements that start with "fluent-" * silenceUnknownJsxElementUsage: (tagName) => tagName.startsWith("fluent-") * ``` */ readonly silenceUnknownJsxElementUsage?: (tagName: string, fileName: string, context: CompilerContext) => boolean; }; export type ServeEnvironmentOptions = { /** * By default, web component peer dependencies and @arcgis/core are * automatically injected when you are running your package in a test * or serve environment (`npx vite`, `npx vitest`, or storybook serve). * * If some additional web component dependencies are needed in test/serve * environment only, instead of declaring those as peer dependencies, you * should add them as `devDependencies` and list them here to have their * custom elements loaded in the test environment. * * @example * Your package does not depend on `@arcgis/map-components`, but for testing * purposes you wish to load Map Components when testing your package in * `index.html`. * In such case, you can set `extraDependencies: ["@arcgis/map-components"]` */ readonly extraDependencies?: (DetailedExtraDependency | string)[]; }; export type DetailedExtraDependency = { /** @example "@arcgis/map-config-components" */ name: string; /** * Overwrite the default CDN asset path for this dependency. * * @example (): string => `https://example.com/arcgis-components/builds/main/cdn/map-config-components/` */ getCdnUrl: (context: CompilerContext) => string; }; /** * @deprecated * For new projects, use Vitest browser mode instead of Puppeteer. * See documentation: https://webgis.esri.com/references/lumina/testing */ export type PuppeteerTestingOptions = { /** * @default false * @deprecated * For new projects, use Vitest browser mode instead of Puppeteer. * See documentation: https://webgis.esri.com/references/lumina/testing */ readonly enabled?: boolean; /** * Additional number of milliseconds to wait in each `page.waitForChanges()` * call after all components re-rendered. The default in Stencil was `100ms`. * Setting this to `false` will speed up the tests. * * @default 0 */ readonly waitForChangesDelay?: number; /** * @example * ```ts * import type { LaunchOptions } from "puppeteer"; * // ... * launchOptions: { * args: ["--no-sandbox", "--disable-setuid-sandbox"], * } satisfies LaunchOptions; * ``` */ readonly launchOptions?: Record; }; export type GenerateDocumentationOptions = { /** * @deprecated Create "api-extractor.config.ts" and use `documentation.copyDocDefinitions` instead. [Example config](https://webgis.esri.com/webgis/core/core/documenting-api#copydoc). */ readonly copyDefinitions?: CopyDocDefinitions; /** * @deprecated Create "api-extractor.config.ts" and use `context.apiJsonEmitPath` instead. [Example config](https://webgis.esri.com/webgis/core/core/documenting-api#copydoc). */ readonly apiJsonFileName?: string | false; /** * The name of the file to write the Stencil-like docs JSON to. * For backwards compatibility. * * @deprecated Use api.json instead. * @default "docs/docs.json" * @see https://stenciljs.com/docs/docs-json */ readonly stencilLikeDocsJsonFileName?: string | false; /** * @deprecated Create "api-extractor.config.ts" and use `documentation.publicStoryUrlPrefix` instead. [Example config](https://webgis.esri.com/webgis/core/core/documenting-api#copydoc). */ readonly publicStoryUrlPrefix?: string; /** * @deprecated Create "api-extractor.config.ts" and use `documentation.getComponentDocsUrl` instead. [Example config](https://webgis.esri.com/webgis/core/core/documenting-api#copydoc). */ readonly getComponentDocsUrl?: (tagName: string, className: string) => string | undefined; /** * @deprecated Create "api-extractor.config.ts" and use `documentation.getComponentDemoUrl` instead. [Example config](https://webgis.esri.com/webgis/core/core/documenting-api#copydoc). */ readonly getComponentDemoUrl?: (tagName: string, className: string) => string | undefined; /** * Additional options for web component documentation for VS Code. This * documentation provides intellisense when editing .html and .css files. * * @see https://devtopia.esri.com/WebGIS/arcgis-web-components/issues/1226 */ readonly vsCode?: VsCodeDocOptions | false; /** * Additional options for the web-types.json generation * * @see https://github.com/JetBrains/web-types */ readonly jetBrains?: WebTypesOptions | false; }; export type GenerateTypesOptions = { /** * @deprecated Create "api-extractor.config.ts" and use `types.compilerOptions` instead. [Example config](https://webgis.esri.com/webgis/core/core/documenting-api#copydoc). */ readonly compilerOptions?: ResolvedApiExtractorConfig["types"]["compilerOptions"]; /** * @deprecated Create "api-extractor.config.ts" and use `types.typeScriptConfigPath` instead. [Example config](https://webgis.esri.com/webgis/core/core/documenting-api#copydoc). */ readonly tsconfigPath?: string; /** * @deprecated Create "api-extractor.config.ts" and use `types.afterDiagnostic` instead. [Example config](https://webgis.esri.com/webgis/core/core/documenting-api#copydoc). */ readonly afterDiagnostic?: (diagnostics: readonly ts.Diagnostic[]) => Promise | void; /** * @deprecated Create "api-extractor.config.ts" and use `types.typeScriptInstanceCreated` instead. [Example config](https://webgis.esri.com/webgis/core/core/documenting-api#copydoc). */ readonly typeScriptInstanceCreated?: (program: ts.Program, host: ts.CompilerHost) => void; /** * @deprecated Create "api-extractor.config.ts" and use `types.declarationTextTransformers` instead. [Example config](https://webgis.esri.com/webgis/core/core/documenting-api#copydoc). */ readonly declarationTextTransformers?: DeclarationTextTransformer[]; /** * Allows to modify the .tsx files as TypeScript AST nodes before they are * emitted. * * This is useful for advanced transformations. * * @remarks * It is advisable that your transformer preserves as many of the original * source file nodes as possible as that will help in source map generation. * Thus where possible, instead of using ts.factory.create* functions, use * the ts.factory.update* functions. * * @remarks * Any changes done in this transformer will not affect the generated typings. * If you wish your changes to apply to typings too, you will need to * implement a separate typings transformer * * @remarks * If you wish to modify the code as a text string rather than AST, then add * a Vite plugin to your vite.config.ts that will implement a transform() * hook. In such case, do not forget to generate a source map for your changes. * See https://rollupjs.org/plugin-development/#source-code-transformations */ readonly sourceFileTransformers?: FileTransformer[]; }; /** * @deprecated */ export type DeclarationTextTransformer = (original: DeclarationFile, context: CompilerContext) => DeclarationFile | false; /** * @deprecated */ export type DeclarationFile = { filePath: string; content: string; }; /** * @example * ```ts * (sourceFile, context) => * context.apiModule === undefined || !sourceFile.text.includes("@method") * ? sourceFile.statements * : sourceFile.statements.map((statement) => * statementVisitor(statement, context) * ); * ``` */ export type FileTransformer = (sourceFile: ts.SourceFile, context: FileTransformContext) => readonly ts.Statement[]; export type FileTransformContext = { readonly compiler: CompilerContext; readonly transformation: ts.TransformationContext; /** * The api.json for this file. * if this file did not define any components, then this will be undefined. */ readonly apiModule: ApiModule | undefined; }; /** @deprecated */ export type ApiJsonOptions = { /** * @deprecated Create "api-extractor.config.ts" and use `afterCreate` instead. [Example config](https://webgis.esri.com/webgis/core/core/documenting-api#copydoc). */ readonly afterCreate?: (api: ApiJson, /** Whether we are patching an existing api.json in response to HRM */ isPatchOnly: boolean) => void; }; export type CssHandlingOptions = { /** * Path to a styles file that you wish to be applied to the entire page, even * outside the shadow dom. Useful for declaring CSS variables. * * These styles will be applied automatically in the CDN build, thus users * do not need a separate `` tag. In the NPM build, users will need to * manually apply these styles as appropriate in their bundler (either via * import statement, or via `` tag or etc). * * @example * // If relative path is provided, it is relative to the root directory. * "./src/styles/global.css" */ globalStylesPath?: string; /** * Styles that you wish to make available in each component's shadow root, but * to not apply outside the shadow root. * * These styles do no apply to non-shadow-root components. * * @remarks * If you are using SCSS, you can also use the following Vite config option to * add a common SCSS import to every SCSS file: * ```tsx * css: { * preprocessorOptions: { * scss: { * // Add "includes.scss" import to each scss file * additionalData(code, id) { * const commonCss = "/src/assets/styles/includes"; * if (!id.endsWith(".scss") || id.endsWith(`${commonCss}.sass`)) { * return undefined; * } * return `@import "${commonCss}";\n${code}`; * }, * }, * }, * } * ``` */ commonStylesPath?: string; /** * The attribute that indicates a component has rendered it's content. * Usually this attribute is added after your component's `loaded()` lifecycle * happened. However, during SSR, "hydrated" is added right away on the * server. * * Until element has this attribute, it will have `visibility:hidden` style * applied. * * @default "hydrated" */ hydratedAttribute?: string; }; export type AssetHandlingOptions = { /** * Any additional assets you wish to provide, besides those located in the * assets/ directories * * @example * ```ts * import resolvePkg from "resolve-pkg"; * * // ... * * extra: [ * { * type: "directory", * source: resolvePkg("@arcgis/arcade-sdk/dist/arcgis-arcade-editor")!, * destination: "arcade-language", * }, * ] * ``` * * @remarks * If you need additional flexibility, you can call the * `compilerContext.provideAssets()` utility at any time * * ```ts * // vite.config.ts * import { defineConfig } from "vite"; * import { useLumina } from "@arcgis/lumina-compiler"; * * export default defineConfig((env) => { * const lumina = useLumina(); * * lumina.context.provideAssets([ * { * type: "dynamic", * source: () => new Date().toString(), * destination: "build_date.txt", * }, * ]); * * return { * plugins: [lumina], * }; * }); * ``` */ readonly extra?: (AssetSpec | ProvideAssetsOptions)[]; /** * The default asset path to use in NPM and CDN builds. * * @default require("@arcgis/lumina-compiler").inferCdnUrl() */ readonly defaultPath?: string; /** * Asset transformers to apply to assets in the assets/ directories in each * component and to the assets/ directory in the package root. * * @example * Example use cases: * - minify the assets in production builds * - transform the asset files * - throw errors if detected incorrect assets */ readonly transformers?: AssetTransformer[]; }; /** * Provide additional assets to be copied to the output directory during build * or served during development. */ export type ProvideAssetsOptions = { readonly assets: AssetSpec[]; readonly transformers?: AssetTransformer[]; /** * By default, provided assets are available both to the dev server and are * written to dist/ * You can optionally configure to only apply the assets to the dev server or * only to the build. */ readonly apply?: CompilerContext["viteCommand"]; }; export type AssetSpec = AssetDirectory | AssetDynamic | AssetFile; type AssetDirectory = { readonly type: "directory"; /** * @example src/components/root/assets */ readonly source: string; /** * Base directory is /dist/cdn-folder-name/assets * * @example /root * @example ../some-folder-outside-assets-dir/ */ readonly destination: string; }; type AssetFile = { readonly type: "file"; /** * @example docs.json */ readonly source: string; /** * Base directory is /dist/cdn-folder-name/assets * * @example docs.json */ readonly destination: string; }; type AssetDynamic = { readonly type: "dynamic"; /** * @example () => new Date().toString() */ readonly source: () => Promise | string; readonly destination: string; }; /** * Transform static assets before serving then in development or before writing * them to dist/ in production. * Example use case: minifying JSON files, compressing SVGs, compressing images. * * @remarks * Both in build and serve mode, the destination will be an absolute path to a * file in the dist/ directory. The path will use POSIX path separators. */ export type AssetTransformer = { readonly match: (destination: string, context: CompilerContext) => boolean; /** * Assuming for now that transformer will transform text files only. * Also, for now only the first matching transformer will be used. */ readonly transform: (fileContent: string, destination: string, context: CompilerContext) => string; }; export type LintingOptions = { /** * Lumina includes build-time checks for common issues and possible bugs. * Most of those should be immediately addressable. However, some (like * updating default value for boolean properties) may require a breaking * change. * * This option exists to temporarily turn these errors into warnings. * @example * To silence an error in a file, add an entry like the following with the * relative path to the file: * * ```ts * booleanMustDefaultFalse: ["src/components/counter/counter.tsx"] * ``` */ silence: { /** * Lumina components may declare required properties using `@required` JSDoc * tag. * * Required properties are enforced in public TypeScript typings. However, * they are not enforced by TypeScript within the package itself due to * technical limitations. Instead, this rule exists to enforce them at * build-time. * * [Documentation](https://webgis.esri.com/references/lumina/properties#string-properties) */ mustIncludeAllRequiredProperties?: string[]; }; }; export {};