Skip to content

Commit

Permalink
feat(vite-plugin-angular): enable .analog support (#870)
Browse files Browse the repository at this point in the history
  • Loading branch information
nartc authored Jan 26, 2024
1 parent 6f59c0c commit 5e09ab5
Show file tree
Hide file tree
Showing 25 changed files with 223 additions and 181 deletions.
2 changes: 1 addition & 1 deletion .prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"singleQuote": true,
"overrides": [
{
"files": "*.ng",
"files": "*.analog",
"options": {
"parser": "html"
}
Expand Down
2 changes: 1 addition & 1 deletion apps/ng-app/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"sourceRoot": "apps/ng-app/src",
"tags": [],
"targets": {
"builds": {
"build": {
"executor": "@nx/vite:build",
"outputs": [
"{options.outputPath}",
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@
import { RouterOutlet, RouterLink } from '@angular/router';
import { delay } from 'rxjs';

import Hello from './hello.ng';
import AnotherOne from './another-one.ng';
import Highlight from './highlight.ng';
import Hello from './hello.analog';
import AnotherOne from './another-one.analog';
import Highlight from './highlight.analog';
import External from './external/external.analog';
import { HelloOriginal } from './hello';

defineMetadata({
Expand Down Expand Up @@ -54,6 +55,8 @@
</script>

<template>
<External />

@if (counter() > 5) {
<Hello [text]="text()" (clicked)="onClick($event)" />
<AnotherOne />
Expand Down
File renamed without changes.
3 changes: 3 additions & 0 deletions apps/ng-app/src/app/external/external-two.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
h1 {
color: darkblue;
}
10 changes: 10 additions & 0 deletions apps/ng-app/src/app/external/external.analog
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<script lang="ts">
import { signal } from '@angular/core';

defineMetadata({
templateUrl: './external.html',
styleUrls: ['./external.css', './external-two.css'],
})

const value = signal('');
</script>
4 changes: 4 additions & 0 deletions apps/ng-app/src/app/external/external.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
p {
color: tomato;
background: #eee;
}
5 changes: 5 additions & 0 deletions apps/ng-app/src/app/external/external.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<div>
<h1>I am in external template!!!</h1>
<input [value]="value()" (input)="value.set($any($event.target).value)" />
<p>Value: {{ value() }}</p>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
afterNextRender,
} from '@angular/core';

import { myFunc } from './export-stuff.ng';
import { myFunc } from './export-stuff.analog';

defineMetadata({
queries: {
Expand Down
3 changes: 2 additions & 1 deletion apps/ng-app/src/app/hello.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Component } from '@angular/core';
import Hello from './hello.ng';
import Hello from './hello.analog';

@Component({
selector: 'app-hello-original',
Expand All @@ -9,6 +9,7 @@ import Hello from './hello.ng';
<p>Below me is a cool hello though</p>
<Hello text="this is from the boring HelloOriginal" />
`,
styles: `p { color: teal; }`,
imports: [Hello],
})
// eslint-disable-next-line @angular-eslint/component-class-suffix
Expand Down
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts">
import { RouteMeta } from '@analogjs/router';
import Hello from '../hello.ng';
import Hello from '../hello.analog';

export const routeMeta: RouteMeta = {
title: 'My page',
Expand Down
2 changes: 1 addition & 1 deletion apps/ng-app/src/main.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'zone.js';
import { bootstrapApplication } from '@angular/platform-browser';
import { appConfig } from './app/app.config';
import AppComponent from './app/app.component.ng';
import AppComponent from './app/app.component.analog';

bootstrapApplication(AppComponent, appConfig).catch((err) =>
console.error(err)
Expand Down
9 changes: 3 additions & 6 deletions apps/ng-app/src/vite-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,8 @@ declare global {
metadata: Omit<
Component,
| 'template'
| 'templateUrl'
| 'standalone'
| 'changeDetection'
| 'styleUrls'
| 'styleUrl'
| 'styles'
| 'outputs'
| 'inputs'
Expand All @@ -34,15 +31,15 @@ declare global {
/**
* Invoke the callback when the component is initialized.
*/
onInit: () => void;
onInit: (initFn: () => void) => void;
/**
* Invoke the callback when the component is destroyed.
*/
onDestroy: () => void;
onDestroy: (destroyFn: () => void) => void;
}
}

declare module '*.ng' {
declare module '*.analog' {
const cmp = any;
export default cmp;
}
5 changes: 5 additions & 0 deletions apps/ng-app/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,11 @@ export default defineConfig(({ mode }) => ({
plugins: [
analog({
ssr: false,
vite: {
experimental: {
supportAnalogFormat: true,
},
},
}),
],
test: {
Expand Down
13 changes: 5 additions & 8 deletions packages/create-analog/template-angular-v17/src/vite-env.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/// <reference types="vite/client" />

// Uncomment the lines below to enable types for experimental .ng format support
// Uncomment the lines below to enable types for experimental .analog format support
// declare global {
// import type { Component } from '@angular/core';
//
Expand All @@ -13,28 +13,25 @@
// metadata: Omit<
// Component,
// | 'template'
// | 'templateUrl'
// | 'standalone'
// | 'changeDetection'
// | 'styleUrls'
// | 'styleUrl'
// | 'styles'
// | 'ouputs'
// | 'outputs'
// | 'inputs'
// > & { exposes?: unknown[] }
// ) => void;
// /**
// * Invoke the callback when the component is initialized.
// */
// onInit: () => void;
// onInit: (initFn: () => void) => void;
// /**
// * Invoke the callback when the component is destroyed.
// */
// onDestroy: () => void;
// onDestroy: (destroyFn: () => void) => void;
// }
// }

// declare module '*.ng' {
// declare module '*.analog' {
// const cmp = any;
// export default cmp;
// }
8 changes: 4 additions & 4 deletions packages/router/src/lib/routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ const FILES = import.meta.glob<RouteExport>([
'/app/routes/**/*.ts',
'/src/app/routes/**/*.ts',
'/src/app/pages/**/*.page.ts',
'/src/app/pages/**/*.page.ng',
'/src/app/pages/**/*.page.analog',
]);

const CONTENT_FILES = import.meta.glob<string>(
Expand Down Expand Up @@ -124,7 +124,7 @@ function toRawPath(filename: string): string {
return filename
.replace(
// convert to relative path and remove file extension
/^\/(.*?)\/routes\/|^\/(.*?)\/pages\/|\/app\/routes\/|(\.page\.(js|ts|ng)$)|(\.(ts|md|ng)$)/g,
/^\/(.*?)\/routes\/|^\/(.*?)\/pages\/|\/app\/routes\/|(\.page\.(js|ts|analog)$)|(\.(ts|md|analog)$)/g,
''
)
.replace(/\[\.{3}.+\]/, '**') // [...not-found] => **
Expand Down Expand Up @@ -157,13 +157,13 @@ function toRoutes(rawRoutes: RawRoute[], files: Files): Route[] {
: (files[rawRoute.filename] as () => Promise<RouteExport>);

const endpointKey = rawRoute.filename.replace(
/\.page\.(ts|ng)$/,
/\.page\.(ts|analog)$/,
ENDPOINT_EXTENSION
);

// get endpoint path
const rawEndpoint = rawRoute.filename
.replace(/\.page\.(ts|ng)$/, '')
.replace(/\.page\.(ts|analog)$/, '')
.replace(/\[\.{3}.+\]/, '**') // [...not-found] => **
.split(APP_DIR)[1];

Expand Down
2 changes: 1 addition & 1 deletion packages/vite-plugin-angular/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { angular } from './lib/angular-vite-plugin';
export { PluginOptions } from './lib/angular-vite-plugin';
export { compileNgFile } from './lib/authoring/ng';
export { compileAnalogFile } from './lib/authoring/analog';

export default angular;
65 changes: 35 additions & 30 deletions packages/vite-plugin-angular/src/lib/angular-vite-plugin.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
import { CompilerHost, NgtscProgram } from '@angular/compiler-cli';
import { transformAsync } from '@babel/core';
import * as path from 'path';

import * as ts from 'typescript';
import {
ModuleNode,
normalizePath,
Expand All @@ -6,11 +11,9 @@ import {
UserConfig,
ViteDevServer,
} from 'vite';
import { CompilerHost, NgtscProgram } from '@angular/compiler-cli';
import { transformAsync } from '@babel/core';

import * as ts from 'typescript';
import * as path from 'path';
import { buildOptimizerPlugin } from './angular-build-optimizer-plugin';
import { jitPlugin } from './angular-jit-plugin';
import { angularVitestPlugin } from './angular-vitest-plugin';

import { createCompilerPlugin } from './compiler-plugin';
import {
Expand All @@ -20,15 +23,12 @@ import {
TemplateUrlsResolver,
} from './component-resolvers';
import { augmentHostWithResources } from './host';
import { jitPlugin } from './angular-jit-plugin';
import { buildOptimizerPlugin } from './angular-build-optimizer-plugin';
import {
loadEsmModule,
angularApplicationPreset,
createJitResourceTransformer,
loadEsmModule,
SourceFileCache,
} from './utils/devkit';
import { angularVitestPlugin } from './angular-vitest-plugin';

export interface PluginOptions {
tsconfig?: string;
Expand All @@ -41,7 +41,12 @@ export interface PluginOptions {
*/
tsTransformers?: ts.CustomTransformers;
};
experimental?: {};
experimental?: {
/**
* Enable experimental support for Analog file extension
*/
supportAnalogFormat?: boolean;
};
supportedBrowsers?: string[];
transformFilter?: (code: string, id: string) => boolean;
}
Expand All @@ -59,7 +64,7 @@ type FileEmitter = (file: string) => Promise<EmitFileResult | undefined>;
* Match .(c or m)ts, .ts extensions with an optional ? for query params
* Ignore .tsx extensions
*/
const TS_EXT_REGEX = /\.[cm]?(ts|ng)[^x]?\??/;
const TS_EXT_REGEX = /\.[cm]?(ts|analog)[^x]?\??/;

export function angular(options?: PluginOptions): Plugin[] {
/**
Expand All @@ -84,7 +89,7 @@ export function angular(options?: PluginOptions): Plugin[] {
},
supportedBrowsers: options?.supportedBrowsers ?? ['safari 15'],
jit: options?.jit,
supportNgFormat: false,
supportAnalogFormat: options?.experimental?.supportAnalogFormat ?? false,
};

// The file emitter created during `onStart` that will be used during the build in `onLoad` callbacks for TS files
Expand Down Expand Up @@ -238,10 +243,11 @@ export function angular(options?: PluginOptions): Plugin[] {
/**
* Check for options.transformFilter
*/
if (options?.transformFilter) {
if (!(options?.transformFilter(code, id) ?? true)) {
return;
}
if (
options?.transformFilter &&
!(options?.transformFilter(code, id) ?? true)
) {
return;
}

/**
Expand Down Expand Up @@ -339,8 +345,8 @@ export function angular(options?: PluginOptions): Plugin[] {
const useInputSourcemap = (!isProd ? undefined : false) as undefined;

if (
id.includes('.ng') &&
pluginOptions.supportNgFormat &&
id.includes('.analog') &&
pluginOptions.supportAnalogFormat &&
fileEmitter
) {
sourceFileCache.invalidate([`${id}.ts`]);
Expand Down Expand Up @@ -404,26 +410,25 @@ export function angular(options?: PluginOptions): Plugin[] {
}),
].filter(Boolean) as Plugin[];

function findNgFiles(config: UserConfig) {
if (!pluginOptions.supportNgFormat) {
function findAnalogFiles(config: UserConfig) {
if (!pluginOptions.supportAnalogFormat) {
return [];
}

const fg = require('fast-glob');
const root = normalizePath(
path.resolve(pluginOptions.workspaceRoot, config.root || '.')
);
const ngFiles: string[] = fg
.sync([`${root}/**/*.ng`], {

return fg
.sync([`${root}/**/*.analog`], {
dot: true,
})
.map((file: string) => `${file}.ts`);

return ngFiles;
}

function setupCompilation(config: UserConfig) {
const ngFiles = findNgFiles(config);
const analogFiles = findAnalogFiles(config);
const { options: tsCompilerOptions, rootNames: rn } =
compilerCli.readConfiguration(pluginOptions.tsconfig, {
suppressOutputPathCheck: true,
Expand All @@ -438,14 +443,14 @@ export function angular(options?: PluginOptions): Plugin[] {
supportTestBed: false,
});

if (pluginOptions.supportNgFormat) {
if (pluginOptions.supportAnalogFormat) {
// Experimental Local Compilation is necessary
// for the Angular compiler to work with
// AOT and virtually compiled .ng files.
// AOT and virtually compiled .analog files.
tsCompilerOptions.compilationMode = 'experimental-local';
}

rootNames = rn.concat(ngFiles);
rootNames = rn.concat(analogFiles);
compilerOptions = tsCompilerOptions;
host = ts.createIncrementalCompilerHost(compilerOptions);

Expand All @@ -456,8 +461,8 @@ export function angular(options?: PluginOptions): Plugin[] {
if (!jit) {
augmentHostWithResources(host, styleTransform, {
inlineStylesExtension: pluginOptions.inlineStylesExtension,
supportNgFormat: pluginOptions.supportNgFormat,
isProd: isProd,
supportAnalogFormat: pluginOptions.supportAnalogFormat,
isProd,
});
}
}
Expand Down
Loading

0 comments on commit 5e09ab5

Please sign in to comment.