diff --git a/.prettierignore b/.prettierignore index 1f66d0f01..3cae2c2be 100644 --- a/.prettierignore +++ b/.prettierignore @@ -2,4 +2,5 @@ /dist /coverage -*.geojson.json \ No newline at end of file +*.geojson.json +.angular diff --git a/apps/picsa-apps/extension-app/project.json b/apps/picsa-apps/extension-app/project.json index e228ed557..43f8513af 100644 --- a/apps/picsa-apps/extension-app/project.json +++ b/apps/picsa-apps/extension-app/project.json @@ -74,6 +74,12 @@ "input": "apps/picsa-tools/monitoring-tool/src/assets", "output": "assets" }, + + { + "glob": "**/*", + "input": "apps/picsa-tools/option-tool/src/assets", + "output": "assets" + }, { "glob": "**/*", "input": "apps/picsa-tools/resources-tool/src/assets", @@ -81,7 +87,7 @@ }, { "glob": "**/*", - "input": "apps/picsa-tools/option-tool/src/assets", + "input": "apps/picsa-tools/seasonal-calendar-tool/src/assets", "output": "assets" }, { diff --git a/apps/picsa-apps/extension-app/src/app/app-routing.module.ts b/apps/picsa-apps/extension-app/src/app/app-routing.module.ts index f176bbdb9..3f047bea2 100644 --- a/apps/picsa-apps/extension-app/src/app/app-routing.module.ts +++ b/apps/picsa-apps/extension-app/src/app/app-routing.module.ts @@ -8,6 +8,7 @@ import { ManualToolModule } from '@picsa/manual/src/app/app.module-embedded'; import { MonitoringToolModule } from '@picsa/monitoring/src/app/app.module-embedded'; import { OptionsToolModule } from '@picsa/option/src/app/app.module-embedded'; import { ResourcesToolModule } from '@picsa/resources/src/app/app.module-embedded'; +import { SeasonalCalendarToolModule } from '@picsa/seasonal-calendar/src/app/app.module-embedded'; const routes: Routes = [ // support embed of budget tool app @@ -41,6 +42,11 @@ const routes: Routes = [ path: 'resources', loadChildren: () => import('@picsa/resources/src/app/app.module-embedded').then((mod) => mod.ResourcesToolModule), }, + { + path: 'seasonal-calendar', + loadChildren: () => + import('@picsa/seasonal-calendar/src/app/app.module-embedded').then((mod) => mod.SeasonalCalendarToolModule), + }, { path: '', loadChildren: () => import('./pages/home/home.module').then((mod) => mod.HomePageModule), @@ -75,6 +81,7 @@ const routes: Routes = [ MonitoringToolModule.forRoot({ urlPrefix: 'monitoring' }), OptionsToolModule.forRoot({ urlPrefix: 'option' }), ResourcesToolModule.forRoot({ urlPrefix: 'resources' }), + SeasonalCalendarToolModule.forRoot({ urlPrefix: 'seasonal-calendar' }), ], exports: [RouterModule], }) diff --git a/apps/picsa-apps/extension-app/src/app/material.module.ts b/apps/picsa-apps/extension-app/src/app/material.module.ts index 68788b73f..f84baf5c6 100644 --- a/apps/picsa-apps/extension-app/src/app/material.module.ts +++ b/apps/picsa-apps/extension-app/src/app/material.module.ts @@ -28,6 +28,7 @@ export class ExtensionToolkitMaterialModule { play_store: 'play_store', probability_tool: 'probability_tool', resources_tool: 'resources_tool', + seasonal_calendar_tool: 'seasonal_calendar_tool', tutorial: 'tutorial', whatsapp: 'whatsapp', }; diff --git a/apps/picsa-apps/extension-app/src/app/pages/home/home.page.html b/apps/picsa-apps/extension-app/src/app/pages/home/home.page.html index 0c5ee5500..7c71961b3 100644 --- a/apps/picsa-apps/extension-app/src/app/pages/home/home.page.html +++ b/apps/picsa-apps/extension-app/src/app/pages/home/home.page.html @@ -7,6 +7,7 @@
\ No newline at end of file diff --git a/apps/picsa-tools/seasonal-calendar-tool-e2e/.eslintrc.json b/apps/picsa-tools/seasonal-calendar-tool-e2e/.eslintrc.json new file mode 100644 index 000000000..73b7aff80 --- /dev/null +++ b/apps/picsa-tools/seasonal-calendar-tool-e2e/.eslintrc.json @@ -0,0 +1,13 @@ +{ + "extends": ["plugin:cypress/recommended", "../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts", "*.tsx", "*.js", "*.jsx"], + "rules": { + "@angular-eslint/component-selector": ["off"], + "@angular-eslint/component-class-suffix": ["off"] + } + } + ] +} diff --git a/apps/picsa-tools/seasonal-calendar-tool-e2e/cypress.config.ts b/apps/picsa-tools/seasonal-calendar-tool-e2e/cypress.config.ts new file mode 100644 index 000000000..bc91e98d8 --- /dev/null +++ b/apps/picsa-tools/seasonal-calendar-tool-e2e/cypress.config.ts @@ -0,0 +1,6 @@ +import { defineConfig } from 'cypress'; +import { nxE2EPreset } from '@nx/cypress/plugins/cypress-preset'; + +export default defineConfig({ + e2e: nxE2EPreset(__dirname), +}); diff --git a/apps/picsa-tools/seasonal-calendar-tool-e2e/project.json b/apps/picsa-tools/seasonal-calendar-tool-e2e/project.json new file mode 100644 index 000000000..9c6bda868 --- /dev/null +++ b/apps/picsa-tools/seasonal-calendar-tool-e2e/project.json @@ -0,0 +1,33 @@ +{ + "name": "seasonal-calendar-tool-e2e", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "sourceRoot": "apps/picsa-tools/seasonal-calendar-tool-e2e/src", + "projectType": "application", + "targets": { + "e2e": { + "executor": "@nx/cypress:cypress", + "options": { + "cypressConfig": "apps/picsa-tools/seasonal-calendar-tool-e2e/cypress.config.ts", + "devServerTarget": "seasonal-calendar-tool:serve:development", + "testingType": "e2e" + }, + "configurations": { + "production": { + "devServerTarget": "seasonal-calendar-tool:serve:production" + }, + "ci": { + "devServerTarget": "seasonal-calendar-tool:serve-static" + } + } + }, + "lint": { + "executor": "@nx/linter:eslint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": ["apps/picsa-tools/seasonal-calendar-tool-e2e/**/*.{js,ts}"] + } + } + }, + "tags": [], + "implicitDependencies": ["seasonal-calendar-tool"] +} diff --git a/apps/picsa-tools/seasonal-calendar-tool-e2e/src/e2e/app.cy.ts b/apps/picsa-tools/seasonal-calendar-tool-e2e/src/e2e/app.cy.ts new file mode 100644 index 000000000..d2f4ea1b6 --- /dev/null +++ b/apps/picsa-tools/seasonal-calendar-tool-e2e/src/e2e/app.cy.ts @@ -0,0 +1,13 @@ +import { getGreeting } from '../support/app.po'; + +describe('seasonal-calendar-tool', () => { + beforeEach(() => cy.visit('/')); + + it('should display welcome message', () => { + // Custom command example, see `../support/commands.ts` file + cy.login('my-email@something.com', 'myPassword'); + + // Function helper example, see `../support/app.po.ts` file + getGreeting().contains('Welcome seasonal-calendar-tool'); + }); +}); diff --git a/apps/picsa-tools/seasonal-calendar-tool-e2e/src/fixtures/example.json b/apps/picsa-tools/seasonal-calendar-tool-e2e/src/fixtures/example.json new file mode 100644 index 000000000..294cbed6c --- /dev/null +++ b/apps/picsa-tools/seasonal-calendar-tool-e2e/src/fixtures/example.json @@ -0,0 +1,4 @@ +{ + "name": "Using fixtures to represent data", + "email": "hello@cypress.io" +} diff --git a/apps/picsa-tools/seasonal-calendar-tool-e2e/src/support/app.po.ts b/apps/picsa-tools/seasonal-calendar-tool-e2e/src/support/app.po.ts new file mode 100644 index 000000000..329342469 --- /dev/null +++ b/apps/picsa-tools/seasonal-calendar-tool-e2e/src/support/app.po.ts @@ -0,0 +1 @@ +export const getGreeting = () => cy.get('h1'); diff --git a/apps/picsa-tools/seasonal-calendar-tool-e2e/src/support/commands.ts b/apps/picsa-tools/seasonal-calendar-tool-e2e/src/support/commands.ts new file mode 100644 index 000000000..310f1fa0e --- /dev/null +++ b/apps/picsa-tools/seasonal-calendar-tool-e2e/src/support/commands.ts @@ -0,0 +1,33 @@ +// *********************************************** +// This example commands.js shows you how to +// create various custom commands and overwrite +// existing commands. +// +// For more comprehensive examples of custom +// commands please read more here: +// https://on.cypress.io/custom-commands +// *********************************************** + +// eslint-disable-next-line @typescript-eslint/no-namespace +declare namespace Cypress { + // eslint-disable-next-line @typescript-eslint/no-unused-vars + interface Chainable { + login(email: string, password: string): void; + } +} +// +// -- This is a parent command -- +Cypress.Commands.add('login', (email, password) => { + console.log('Custom command example: Login', email, password); +}); +// +// -- This is a child command -- +// Cypress.Commands.add("drag", { prevSubject: 'element'}, (subject, options) => { ... }) +// +// +// -- This is a dual command -- +// Cypress.Commands.add("dismiss", { prevSubject: 'optional'}, (subject, options) => { ... }) +// +// +// -- This will overwrite an existing command -- +// Cypress.Commands.overwrite("visit", (originalFn, url, options) => { ... }) diff --git a/apps/picsa-tools/seasonal-calendar-tool-e2e/src/support/e2e.ts b/apps/picsa-tools/seasonal-calendar-tool-e2e/src/support/e2e.ts new file mode 100644 index 000000000..3d469a6b6 --- /dev/null +++ b/apps/picsa-tools/seasonal-calendar-tool-e2e/src/support/e2e.ts @@ -0,0 +1,17 @@ +// *********************************************************** +// This example support/index.js is processed and +// loaded automatically before your test files. +// +// This is a great place to put global configuration and +// behavior that modifies Cypress. +// +// You can change the location of this file or turn off +// automatically serving support files with the +// 'supportFile' configuration option. +// +// You can read more here: +// https://on.cypress.io/configuration +// *********************************************************** + +// Import commands.js using ES2015 syntax: +import './commands'; diff --git a/apps/picsa-tools/seasonal-calendar-tool-e2e/tsconfig.json b/apps/picsa-tools/seasonal-calendar-tool-e2e/tsconfig.json new file mode 100644 index 000000000..80f8b5658 --- /dev/null +++ b/apps/picsa-tools/seasonal-calendar-tool-e2e/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "sourceMap": false, + "outDir": "../../../dist/out-tsc", + "allowJs": true, + "types": ["cypress", "node"], + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src/**/*.ts", "src/**/*.js", "cypress.config.ts"] +} diff --git a/apps/picsa-tools/seasonal-calendar-tool/.eslintrc.json b/apps/picsa-tools/seasonal-calendar-tool/.eslintrc.json new file mode 100644 index 000000000..6161ecdbb --- /dev/null +++ b/apps/picsa-tools/seasonal-calendar-tool/.eslintrc.json @@ -0,0 +1,33 @@ +{ + "extends": ["../../../.eslintrc.json"], + "ignorePatterns": ["!**/*"], + "overrides": [ + { + "files": ["*.ts"], + "extends": ["plugin:@nx/angular", "plugin:@angular-eslint/template/process-inline-templates"], + "rules": { + "@angular-eslint/directive-selector": [ + "warn", + { + "type": "attribute", + "prefix": "picsa", + "style": "camelCase" + } + ], + "@angular-eslint/component-selector": [ + "warn", + { + "type": "element", + "prefix": "seasonal-calendar", + "style": "kebab-case" + } + ], + "@angular-eslint/component-class-suffix": ["warn"] + } + }, + { + "files": ["*.html"], + "extends": ["plugin:@nx/angular-template"] + } + ] +} diff --git a/apps/picsa-tools/seasonal-calendar-tool/jest.config.ts b/apps/picsa-tools/seasonal-calendar-tool/jest.config.ts new file mode 100644 index 000000000..69c81e59a --- /dev/null +++ b/apps/picsa-tools/seasonal-calendar-tool/jest.config.ts @@ -0,0 +1,22 @@ +/* eslint-disable */ +export default { + displayName: 'seasonal-calendar-tool', + preset: '../../../jest.preset.js', + setupFilesAfterEnv: ['/src/test-setup.ts'], + coverageDirectory: '../../../coverage/apps/picsa-tools/seasonal-calendar-tool', + transform: { + '^.+\\.(ts|mjs|js|html)$': [ + 'jest-preset-angular', + { + tsconfig: '/tsconfig.spec.json', + stringifyContentPathRegex: '\\.(html|svg)$', + }, + ], + }, + transformIgnorePatterns: ['node_modules/(?!.*\\.mjs$)'], + snapshotSerializers: [ + 'jest-preset-angular/build/serializers/no-ng-attributes', + 'jest-preset-angular/build/serializers/ng-snapshot', + 'jest-preset-angular/build/serializers/html-comment', + ], +}; diff --git a/apps/picsa-tools/seasonal-calendar-tool/project.json b/apps/picsa-tools/seasonal-calendar-tool/project.json new file mode 100644 index 000000000..fcb7d4ae6 --- /dev/null +++ b/apps/picsa-tools/seasonal-calendar-tool/project.json @@ -0,0 +1,101 @@ +{ + "name": "seasonal-calendar-tool", + "$schema": "../../../node_modules/nx/schemas/project-schema.json", + "projectType": "application", + "prefix": "picsa", + "sourceRoot": "apps/picsa-tools/seasonal-calendar-tool/src", + "tags": [], + "targets": { + "build": { + "executor": "@angular-devkit/build-angular:browser", + "outputs": ["{options.outputPath}"], + "options": { + "outputPath": "dist/apps/picsa-tools/seasonal-calendar-tool", + "index": "apps/picsa-tools/seasonal-calendar-tool/src/index.html", + "main": "apps/picsa-tools/seasonal-calendar-tool/src/main.ts", + "polyfills": ["zone.js"], + "tsConfig": "apps/picsa-tools/seasonal-calendar-tool/tsconfig.app.json", + "assets": [ + "apps/picsa-tools/seasonal-calendar-tool/src/favicon.ico", + "apps/picsa-tools/seasonal-calendar-tool/src/assets" + ], + "styles": ["apps/picsa-tools/seasonal-calendar-tool/src/styles.scss", "libs/theme/src/_index.scss"], + "scripts": [] + }, + "configurations": { + "production": { + "budgets": [ + { + "type": "initial", + "maximumWarning": "500kb", + "maximumError": "2mb" + }, + { + "type": "anyComponentStyle", + "maximumWarning": "2kb", + "maximumError": "4kb" + } + ], + "outputHashing": "all" + }, + "development": { + "buildOptimizer": false, + "optimization": false, + "vendorChunk": true, + "extractLicenses": false, + "sourceMap": true, + "namedChunks": true + } + }, + "defaultConfiguration": "production" + }, + "serve": { + "executor": "@angular-devkit/build-angular:dev-server", + "configurations": { + "production": { + "browserTarget": "seasonal-calendar-tool:build:production" + }, + "development": { + "browserTarget": "seasonal-calendar-tool:build:development" + } + }, + "defaultConfiguration": "development" + }, + "extract-i18n": { + "executor": "@angular-devkit/build-angular:extract-i18n", + "options": { + "browserTarget": "seasonal-calendar-tool:build" + } + }, + "lint": { + "executor": "@nx/linter:eslint", + "outputs": ["{options.outputFile}"], + "options": { + "lintFilePatterns": [ + "apps/picsa-tools/seasonal-calendar-tool/**/*.ts", + "apps/picsa-tools/seasonal-calendar-tool/**/*.html" + ] + } + }, + "test": { + "executor": "@nx/jest:jest", + "outputs": ["{workspaceRoot}/coverage/{projectRoot}"], + "options": { + "jestConfig": "apps/picsa-tools/seasonal-calendar-tool/jest.config.ts", + "passWithNoTests": true + }, + "configurations": { + "ci": { + "ci": true, + "codeCoverage": true + } + } + }, + "serve-static": { + "executor": "@nx/web:file-server", + "options": { + "buildTarget": "seasonal-calendar-tool:build" + } + } + } +} diff --git a/apps/picsa-tools/seasonal-calendar-tool/src/app/app-routing.module.ts b/apps/picsa-tools/seasonal-calendar-tool/src/app/app-routing.module.ts new file mode 100644 index 000000000..b78eca854 --- /dev/null +++ b/apps/picsa-tools/seasonal-calendar-tool/src/app/app-routing.module.ts @@ -0,0 +1,25 @@ +import { NgModule } from '@angular/core'; +import { PreloadAllModules, RouterModule, Routes } from '@angular/router'; + +export const ROUTES_COMMON: Routes = [ + { + path: '', + loadChildren: () => import('./pages/home/home.module').then((m) => m.HomeModule), + title: 'Seasonal Calendar', + }, +]; +/** Routes only registered in standalone mode */ +const ROUTES_STANDALONE: Routes = [{ path: '**', redirectTo: '' }]; + +/******************************************************************* + * Standalone Version + ******************************************************************/ +@NgModule({ + imports: [ + RouterModule.forRoot([...ROUTES_COMMON, ...ROUTES_STANDALONE], { + preloadingStrategy: PreloadAllModules, + }), + ], + exports: [RouterModule], +}) +export class AppRoutingModule {} diff --git a/apps/picsa-tools/seasonal-calendar-tool/src/app/app.component.html b/apps/picsa-tools/seasonal-calendar-tool/src/app/app.component.html new file mode 100644 index 000000000..d5e086097 --- /dev/null +++ b/apps/picsa-tools/seasonal-calendar-tool/src/app/app.component.html @@ -0,0 +1,4 @@ +
+ + +
diff --git a/apps/picsa-tools/seasonal-calendar-tool/src/app/app.component.scss b/apps/picsa-tools/seasonal-calendar-tool/src/app/app.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/apps/picsa-tools/seasonal-calendar-tool/src/app/app.component.spec.ts b/apps/picsa-tools/seasonal-calendar-tool/src/app/app.component.spec.ts new file mode 100644 index 000000000..fd24e303e --- /dev/null +++ b/apps/picsa-tools/seasonal-calendar-tool/src/app/app.component.spec.ts @@ -0,0 +1,26 @@ +import { TestBed } from '@angular/core/testing'; +import { AppComponent } from './app.component'; +import { NxWelcomeComponent } from './nx-welcome.component'; +import { RouterTestingModule } from '@angular/router/testing'; + +describe('AppComponent', () => { + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [RouterTestingModule], + declarations: [AppComponent, NxWelcomeComponent], + }).compileComponents(); + }); + + it('should render title', () => { + const fixture = TestBed.createComponent(AppComponent); + fixture.detectChanges(); + const compiled = fixture.nativeElement as HTMLElement; + expect(compiled.querySelector('h1')?.textContent).toContain('Welcome seasonal-calendar-tool'); + }); + + it(`should have as title 'seasonal-calendar-tool'`, () => { + const fixture = TestBed.createComponent(AppComponent); + const app = fixture.componentInstance; + expect(app.title).toEqual('seasonal-calendar-tool'); + }); +}); diff --git a/apps/picsa-tools/seasonal-calendar-tool/src/app/app.component.ts b/apps/picsa-tools/seasonal-calendar-tool/src/app/app.component.ts new file mode 100644 index 000000000..77deba3b5 --- /dev/null +++ b/apps/picsa-tools/seasonal-calendar-tool/src/app/app.component.ts @@ -0,0 +1,19 @@ +import { Component } from '@angular/core'; + +@Component({ + // eslint-disable-next-line @angular-eslint/component-selector + selector: 'picsa-seasonal-calendar-tool', + templateUrl: './app.component.html', + styleUrls: ['./app.component.scss'], +}) +export class AppComponent { + title = 'seasonal-calendar'; +} + +@Component({ + // eslint-disable-next-line @angular-eslint/component-selector + selector: 'picsa-seasonal-calendar-tool', + template: '', +}) +// eslint-disable-next-line @angular-eslint/component-class-suffix +export class AppComponentEmbedded extends AppComponent {} diff --git a/apps/picsa-tools/seasonal-calendar-tool/src/app/app.module-embedded.ts b/apps/picsa-tools/seasonal-calendar-tool/src/app/app.module-embedded.ts new file mode 100644 index 000000000..e577fb23f --- /dev/null +++ b/apps/picsa-tools/seasonal-calendar-tool/src/app/app.module-embedded.ts @@ -0,0 +1,49 @@ +import { ModuleWithProviders, NgModule } from '@angular/core'; +import { Router, RouterModule } from '@angular/router'; +import { PicsaTranslateService } from '@picsa/shared/modules'; +import { registerEmbeddedRoutes } from '@picsa/utils'; + +import { AppComponentEmbedded } from './app.component'; +import { APP_COMMON_IMPORTS } from './app.module'; +import { ROUTES_COMMON } from './app-routing.module'; + +export class EmbeddedConfig { + /** Path app routed through, e.g. 'budget' */ + urlPrefix: string; +} + +/******************************************************************* + * Routes + ******************************************************************/ +@NgModule({ + imports: [RouterModule.forChild([])], +}) +export class EmbeddedRoutingModule { + constructor(router: Router, embeddedConfig: EmbeddedConfig) { + registerEmbeddedRoutes(ROUTES_COMMON, router, embeddedConfig.urlPrefix); + } +} + +/******************************************************************* + * Module + ******************************************************************/ +@NgModule({ + declarations: [AppComponentEmbedded], + imports: [...APP_COMMON_IMPORTS, EmbeddedRoutingModule], + bootstrap: [AppComponentEmbedded], +}) +export class BaseModule { + // ensure translate has been initiated + constructor(public translate: PicsaTranslateService) {} +} + +/** Use to import directly into another app via lazy-loading */ +@NgModule() +export class SeasonalCalendarToolModule { + static forRoot(config: EmbeddedConfig): ModuleWithProviders { + return { + ngModule: BaseModule, + providers: [PicsaTranslateService, { provide: EmbeddedConfig, useValue: config }], + }; + } +} diff --git a/apps/picsa-tools/seasonal-calendar-tool/src/app/app.module.ts b/apps/picsa-tools/seasonal-calendar-tool/src/app/app.module.ts new file mode 100644 index 000000000..1cee46d5d --- /dev/null +++ b/apps/picsa-tools/seasonal-calendar-tool/src/app/app.module.ts @@ -0,0 +1,43 @@ +import { HttpClientModule } from '@angular/common/http'; +import { NgModule } from '@angular/core'; +import { BrowserModule } from '@angular/platform-browser'; +import { BrowserAnimationsModule, NoopAnimationsModule } from '@angular/platform-browser/animations'; +import { PicsaCommonComponentsModule } from '@picsa/components'; +import { PicsaDb_V2_Module, PicsaTranslateModule, PicsaTranslateService } from '@picsa/shared/modules'; + +import { AppComponent } from './app.component'; +import { AppRoutingModule } from './app-routing.module'; +import { SeasonalCalendarMaterialModule } from './components/material.module'; + +/** Core imports only required when running standalone */ +const StandaloneImports = [ + AppRoutingModule, + BrowserModule, + BrowserAnimationsModule, + NoopAnimationsModule, + PicsaTranslateModule.forRoot(), + PicsaDb_V2_Module.forRoot(), +]; + +/** Common imports used in both standalone and embedded formats */ +export const APP_COMMON_IMPORTS = [ + HttpClientModule, + SeasonalCalendarMaterialModule, + PicsaTranslateModule, + PicsaDb_V2_Module, + PicsaCommonComponentsModule, +]; + +/******************************************************************* + * Standalone Version + ******************************************************************/ +@NgModule({ + declarations: [AppComponent], + imports: [...StandaloneImports, ...APP_COMMON_IMPORTS], + bootstrap: [AppComponent], + schemas: [], +}) +export class AppModule { + // ensure translate service initialised + constructor(public translate: PicsaTranslateService) {} +} diff --git a/apps/picsa-tools/seasonal-calendar-tool/src/app/app.routes.ts b/apps/picsa-tools/seasonal-calendar-tool/src/app/app.routes.ts new file mode 100644 index 000000000..8762dfe2c --- /dev/null +++ b/apps/picsa-tools/seasonal-calendar-tool/src/app/app.routes.ts @@ -0,0 +1,3 @@ +import { Route } from '@angular/router'; + +export const appRoutes: Route[] = []; diff --git a/apps/picsa-tools/seasonal-calendar-tool/src/app/components/components.module.ts b/apps/picsa-tools/seasonal-calendar-tool/src/app/components/components.module.ts new file mode 100644 index 000000000..a2de0913a --- /dev/null +++ b/apps/picsa-tools/seasonal-calendar-tool/src/app/components/components.module.ts @@ -0,0 +1,28 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { RouterModule } from '@angular/router'; +// Shared modules +import { PicsaCommonComponentsModule } from '@picsa/components'; +import { PicsaTranslateModule } from '@picsa/shared/modules'; + +// Local components +import { SeasonalCalendarMaterialModule } from './material.module'; + +const Components = []; + +@NgModule({ + imports: [ + CommonModule, + FormsModule, + PicsaCommonComponentsModule, + PicsaTranslateModule, + ReactiveFormsModule, + RouterModule, + SeasonalCalendarMaterialModule, + ], + exports: [PicsaCommonComponentsModule, SeasonalCalendarMaterialModule, ...Components], + declarations: [Components], + providers: [], +}) +export class SeasonalCalendarToolComponentsModule {} diff --git a/apps/picsa-tools/seasonal-calendar-tool/src/app/components/material.module.ts b/apps/picsa-tools/seasonal-calendar-tool/src/app/components/material.module.ts new file mode 100644 index 000000000..23c52d328 --- /dev/null +++ b/apps/picsa-tools/seasonal-calendar-tool/src/app/components/material.module.ts @@ -0,0 +1,31 @@ +import { NgModule } from '@angular/core'; +import { MatButtonModule } from '@angular/material/button'; +import { MatDialogModule } from '@angular/material/dialog'; +import { MatIconModule } from '@angular/material/icon'; +import { MatIconRegistry } from '@angular/material/icon'; +import { MatSelectModule } from '@angular/material/select'; +import { MatStepperModule } from '@angular/material/stepper'; +import { MatTableModule } from '@angular/material/table'; +import { DomSanitizer } from '@angular/platform-browser'; + +const COMPONENTS = [MatButtonModule, MatDialogModule, MatIconModule, MatSelectModule, MatStepperModule, MatTableModule]; +// use custom module to make it easier to control what is available through app +@NgModule({ + imports: COMPONENTS, + exports: COMPONENTS, +}) +export class SeasonalCalendarMaterialModule { + constructor(private matIconRegistry: MatIconRegistry, private domSanitizer: DomSanitizer) { + this.registerIcons(); + } + // register custom icons from the assets/svgs folder for access within the app + // icons can be accessed in mat-icon as svgIcon='seasonal_calendar_${key}' + registerIcons() { + const icons = {}; + for (const [key, value] of Object.entries(icons)) { + const iconName = `seasonal_calendar_${key}`; + const iconUrl = this.domSanitizer.bypassSecurityTrustResourceUrl(`assets/svgs/${value}.svg`); + this.matIconRegistry.addSvgIcon(iconName, iconUrl); + } + } +} diff --git a/apps/picsa-tools/seasonal-calendar-tool/src/app/pages/home/home.component.html b/apps/picsa-tools/seasonal-calendar-tool/src/app/pages/home/home.component.html new file mode 100644 index 000000000..fedc210f6 --- /dev/null +++ b/apps/picsa-tools/seasonal-calendar-tool/src/app/pages/home/home.component.html @@ -0,0 +1,3 @@ +
+

Seasonal Calendar Home

+
diff --git a/apps/picsa-tools/seasonal-calendar-tool/src/app/pages/home/home.component.scss b/apps/picsa-tools/seasonal-calendar-tool/src/app/pages/home/home.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/apps/picsa-tools/seasonal-calendar-tool/src/app/pages/home/home.component.spec.ts b/apps/picsa-tools/seasonal-calendar-tool/src/app/pages/home/home.component.spec.ts new file mode 100644 index 000000000..8680c5059 --- /dev/null +++ b/apps/picsa-tools/seasonal-calendar-tool/src/app/pages/home/home.component.spec.ts @@ -0,0 +1,21 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; +import { HomeComponent } from './home.component'; + +describe('HomeComponent', () => { + let component: HomeComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [HomeComponent], + }).compileComponents(); + + fixture = TestBed.createComponent(HomeComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/apps/picsa-tools/seasonal-calendar-tool/src/app/pages/home/home.component.ts b/apps/picsa-tools/seasonal-calendar-tool/src/app/pages/home/home.component.ts new file mode 100644 index 000000000..1315887c1 --- /dev/null +++ b/apps/picsa-tools/seasonal-calendar-tool/src/app/pages/home/home.component.ts @@ -0,0 +1,8 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'seasonal-calendar-home', + templateUrl: './home.component.html', + styleUrls: ['./home.component.scss'], +}) +export class HomeComponent {} diff --git a/apps/picsa-tools/seasonal-calendar-tool/src/app/pages/home/home.module.ts b/apps/picsa-tools/seasonal-calendar-tool/src/app/pages/home/home.module.ts new file mode 100644 index 000000000..a1e34b758 --- /dev/null +++ b/apps/picsa-tools/seasonal-calendar-tool/src/app/pages/home/home.module.ts @@ -0,0 +1,19 @@ +import { CommonModule } from '@angular/common'; +import { NgModule } from '@angular/core'; +import { Route, RouterModule } from '@angular/router'; + +import { SeasonalCalendarToolComponentsModule } from '../../components/components.module'; +import { HomeComponent } from './home.component'; + +const routes: Route[] = [ + { + path: '', + component: HomeComponent, + }, +]; + +@NgModule({ + declarations: [HomeComponent], + imports: [CommonModule, RouterModule.forChild(routes), SeasonalCalendarToolComponentsModule], +}) +export class HomeModule {} diff --git a/apps/picsa-tools/seasonal-calendar-tool/src/assets/.gitkeep b/apps/picsa-tools/seasonal-calendar-tool/src/assets/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/apps/picsa-tools/seasonal-calendar-tool/src/favicon.ico b/apps/picsa-tools/seasonal-calendar-tool/src/favicon.ico new file mode 100644 index 000000000..317ebcb23 Binary files /dev/null and b/apps/picsa-tools/seasonal-calendar-tool/src/favicon.ico differ diff --git a/apps/picsa-tools/seasonal-calendar-tool/src/index.html b/apps/picsa-tools/seasonal-calendar-tool/src/index.html new file mode 100644 index 000000000..4a5a43a7f --- /dev/null +++ b/apps/picsa-tools/seasonal-calendar-tool/src/index.html @@ -0,0 +1,13 @@ + + + + + seasonal-calendar-tool + + + + + + + + diff --git a/apps/picsa-tools/seasonal-calendar-tool/src/main.ts b/apps/picsa-tools/seasonal-calendar-tool/src/main.ts new file mode 100644 index 000000000..17a5cd4e0 --- /dev/null +++ b/apps/picsa-tools/seasonal-calendar-tool/src/main.ts @@ -0,0 +1,7 @@ +import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; + +import { AppModule } from './app/app.module'; + +platformBrowserDynamic() + .bootstrapModule(AppModule) + .catch((err) => console.error(err)); diff --git a/apps/picsa-tools/seasonal-calendar-tool/src/styles.scss b/apps/picsa-tools/seasonal-calendar-tool/src/styles.scss new file mode 100644 index 000000000..90d4ee007 --- /dev/null +++ b/apps/picsa-tools/seasonal-calendar-tool/src/styles.scss @@ -0,0 +1 @@ +/* You can add global styles to this file, and also import other style files */ diff --git a/apps/picsa-tools/seasonal-calendar-tool/src/test-setup.ts b/apps/picsa-tools/seasonal-calendar-tool/src/test-setup.ts new file mode 100644 index 000000000..ab1eeeb33 --- /dev/null +++ b/apps/picsa-tools/seasonal-calendar-tool/src/test-setup.ts @@ -0,0 +1,8 @@ +// @ts-expect-error https://thymikee.github.io/jest-preset-angular/docs/getting-started/test-environment +globalThis.ngJest = { + testEnvironmentOptions: { + errorOnUnknownElements: true, + errorOnUnknownProperties: true, + }, +}; +import 'jest-preset-angular/setup-jest'; diff --git a/apps/picsa-tools/seasonal-calendar-tool/tsconfig.app.json b/apps/picsa-tools/seasonal-calendar-tool/tsconfig.app.json new file mode 100644 index 000000000..58220429a --- /dev/null +++ b/apps/picsa-tools/seasonal-calendar-tool/tsconfig.app.json @@ -0,0 +1,10 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "types": [] + }, + "files": ["src/main.ts"], + "include": ["src/**/*.d.ts"], + "exclude": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts"] +} diff --git a/apps/picsa-tools/seasonal-calendar-tool/tsconfig.editor.json b/apps/picsa-tools/seasonal-calendar-tool/tsconfig.editor.json new file mode 100644 index 000000000..8ae117d96 --- /dev/null +++ b/apps/picsa-tools/seasonal-calendar-tool/tsconfig.editor.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*.ts"], + "compilerOptions": { + "types": ["jest", "node"] + } +} diff --git a/apps/picsa-tools/seasonal-calendar-tool/tsconfig.json b/apps/picsa-tools/seasonal-calendar-tool/tsconfig.json new file mode 100644 index 000000000..fbdce5d97 --- /dev/null +++ b/apps/picsa-tools/seasonal-calendar-tool/tsconfig.json @@ -0,0 +1,32 @@ +{ + "compilerOptions": { + "target": "es2022", + "useDefineForClassFields": false, + "forceConsistentCasingInFileNames": true, + "strict": true, + "noImplicitOverride": true, + "noPropertyAccessFromIndexSignature": false, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.app.json" + }, + { + "path": "./tsconfig.spec.json" + }, + { + "path": "./tsconfig.editor.json" + } + ], + "extends": "../../../tsconfig.base.json", + "angularCompilerOptions": { + "enableI18nLegacyMessageIdFormat": false, + "strictInjectionParameters": true, + "strictInputAccessModifiers": true, + "strictTemplates": true + } +} diff --git a/apps/picsa-tools/seasonal-calendar-tool/tsconfig.spec.json b/apps/picsa-tools/seasonal-calendar-tool/tsconfig.spec.json new file mode 100644 index 000000000..e637bf83b --- /dev/null +++ b/apps/picsa-tools/seasonal-calendar-tool/tsconfig.spec.json @@ -0,0 +1,11 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "module": "commonjs", + "target": "es2016", + "types": ["jest", "node"] + }, + "files": ["src/test-setup.ts"], + "include": ["jest.config.ts", "src/**/*.test.ts", "src/**/*.spec.ts", "src/**/*.d.ts"] +} diff --git a/package.json b/package.json index fb0a24967..8aed4eaf1 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "start:monitoring": "nx run picsa-tools-monitoring-tool:serve", "start:option": "nx run picsa-tools-option-tool:serve", "start:resources": "nx run picsa-tools-resources-tool:serve", + "start:seasonal-calendar": "nx run seasonal-calendar-tool:serve", "start:manual": "nx run picsa-tools-manual-tool:serve", "build": "nx run picsa-apps-extension-app-native:build", "build:webcomponents": "yarn nx build picsa-webcomponents", @@ -123,6 +124,7 @@ "@nx/js": "16.8.1", "@nx/linter": "16.8.1", "@nx/nest": "16.8.1", + "@nx/web": "16.8.1", "@nx/workspace": "16.8.1", "@nxext/stencil": "^16.3.0", "@schematics/angular": "16.2.2", @@ -160,6 +162,7 @@ "jest-jasmine2": "29.4.3", "jest-preset-angular": "13.1.1", "jetifier": "^2.0.0", + "jsonc-eslint-parser": "^2.1.0", "lint-staged": "^13.2.1", "ng-packagr": "16.2.3", "nx": "16.8.1", diff --git a/tsconfig.base.json b/tsconfig.base.json index 1196af2e0..33f71327b 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -36,6 +36,7 @@ "@picsa/monitoring/*": ["apps/picsa-tools/monitoring-tool/*"], "@picsa/option/*": ["apps/picsa-tools/option-tool/*"], "@picsa/resources/*": ["apps/picsa-tools/resources-tool/*"], + "@picsa/seasonal-calendar/*": ["apps/picsa-tools/seasonal-calendar-tool/*"], "@picsa/shared": ["libs/shared/src/index.ts"], "@picsa/shared/*": ["libs/shared/src/*"], "@picsa/theme": ["libs/theme/src/index.ts"], diff --git a/yarn.lock b/yarn.lock index a02715593..74057b296 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5525,6 +5525,15 @@ __metadata: languageName: node linkType: hard +"@nrwl/web@npm:16.8.1": + version: 16.8.1 + resolution: "@nrwl/web@npm:16.8.1" + dependencies: + "@nx/web": 16.8.1 + checksum: 405cb941a8dc480fbbec05bea5a3f260d6b67885b3043339eddf09a184620c7cc4c81fd032a40907186eca66e859ccd281ce5237fa58c689b561d1870a24ca7d + languageName: node + linkType: hard + "@nrwl/webpack@npm:16.8.1": version: 16.8.1 resolution: "@nrwl/webpack@npm:16.8.1" @@ -5829,6 +5838,21 @@ __metadata: languageName: node linkType: hard +"@nx/web@npm:16.8.1": + version: 16.8.1 + resolution: "@nx/web@npm:16.8.1" + dependencies: + "@nrwl/web": 16.8.1 + "@nx/devkit": 16.8.1 + "@nx/js": 16.8.1 + chalk: ^4.1.0 + detect-port: ^1.5.1 + http-server: ^14.1.0 + tslib: ^2.3.0 + checksum: 2d7e2e801013cee46dbcc1bca558b907e331808deef4ce9e77d8225cb7dde8f4c4158fad67e0534e5da190f6d1b029c75fe9af94ba1c86483aa01ce86b512b2b + languageName: node + linkType: hard + "@nx/webpack@npm:16.8.1": version: 16.8.1 resolution: "@nx/webpack@npm:16.8.1" @@ -8233,6 +8257,15 @@ __metadata: languageName: node linkType: hard +"async@npm:^2.6.4": + version: 2.6.4 + resolution: "async@npm:2.6.4" + dependencies: + lodash: ^4.17.14 + checksum: a52083fb32e1ebe1d63e5c5624038bb30be68ff07a6c8d7dfe35e47c93fc144bd8652cbec869e0ac07d57dde387aa5f1386be3559cdee799cb1f789678d88e19 + languageName: node + linkType: hard + "async@npm:^3.2.0, async@npm:^3.2.3": version: 3.2.4 resolution: "async@npm:3.2.4" @@ -8521,6 +8554,15 @@ __metadata: languageName: node linkType: hard +"basic-auth@npm:^2.0.1": + version: 2.0.1 + resolution: "basic-auth@npm:2.0.1" + dependencies: + safe-buffer: 5.1.2 + checksum: 3419b805d5dfc518f3a05dcf42aa53aa9ce820e50b6df5097f9e186322e1bc733c36722b624802cd37e791035aa73b828ed814d8362333d42d7f5cd04d7a5e48 + languageName: node + linkType: hard + "basic-ftp@npm:^5.0.2": version: 5.0.3 resolution: "basic-ftp@npm:5.0.3" @@ -9632,6 +9674,13 @@ __metadata: languageName: node linkType: hard +"corser@npm:^2.0.1": + version: 2.0.1 + resolution: "corser@npm:2.0.1" + checksum: 9ff6944eda760c8c3118747a636afc3ede53b41e7b9960513a15b88032209a728e630ae4b41e20a941e34da129fe9094d1f5d95123ef64ac2e16cdad8dce9c87 + languageName: node + linkType: hard + "cosmiconfig@npm:8.0.0": version: 8.0.0 resolution: "cosmiconfig@npm:8.0.0" @@ -10590,7 +10639,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:^3.1.0, debug@npm:^3.2.6": +"debug@npm:^3.1.0, debug@npm:^3.2.6, debug@npm:^3.2.7": version: 3.2.7 resolution: "debug@npm:3.2.7" dependencies: @@ -13128,6 +13177,15 @@ __metadata: languageName: node linkType: hard +"he@npm:^1.2.0": + version: 1.2.0 + resolution: "he@npm:1.2.0" + bin: + he: bin/he + checksum: 3d4d6babccccd79c5c5a3f929a68af33360d6445587d628087f39a965079d84f18ce9c3d3f917ee1e3978916fc833bb8b29377c3b403f919426f91bc6965e7a7 + languageName: node + linkType: hard + "hls.js@npm:^1.4.9": version: 1.4.9 resolution: "hls.js@npm:1.4.9" @@ -13324,6 +13382,29 @@ __metadata: languageName: node linkType: hard +"http-server@npm:^14.1.0": + version: 14.1.1 + resolution: "http-server@npm:14.1.1" + dependencies: + basic-auth: ^2.0.1 + chalk: ^4.1.2 + corser: ^2.0.1 + he: ^1.2.0 + html-encoding-sniffer: ^3.0.0 + http-proxy: ^1.18.1 + mime: ^1.6.0 + minimist: ^1.2.6 + opener: ^1.5.1 + portfinder: ^1.0.28 + secure-compare: 3.0.1 + union: ~0.5.0 + url-join: ^4.0.1 + bin: + http-server: bin/http-server + checksum: 4f9674289195eaf9f3e408e093d2080b0d4647559a32c9e7868639c327cab62efd0bb8bc9ded9a625d9ce982cbb03517d4472400af5ecf36eeb5b4fa62d113fe + languageName: node + linkType: hard + "http-signature@npm:~1.3.6": version: 1.3.6 resolution: "http-signature@npm:1.3.6" @@ -15635,7 +15716,7 @@ __metadata: languageName: node linkType: hard -"lodash@npm:^4.17.21, lodash@npm:^4.7.0": +"lodash@npm:^4.17.14, lodash@npm:^4.17.21, lodash@npm:^4.7.0": version: 4.17.21 resolution: "lodash@npm:4.17.21" checksum: eb835a2e51d381e561e508ce932ea50a8e5a68f4ebdd771ea240d3048244a8d13658acbd502cd4829768c56f2e16bdd4340b9ea141297d472517b83868e677f7 @@ -15949,7 +16030,7 @@ __metadata: languageName: node linkType: hard -"mime@npm:1.6.0, mime@npm:^1.4.1": +"mime@npm:1.6.0, mime@npm:^1.4.1, mime@npm:^1.6.0": version: 1.6.0 resolution: "mime@npm:1.6.0" bin: @@ -16193,6 +16274,17 @@ __metadata: languageName: node linkType: hard +"mkdirp@npm:^0.5.6": + version: 0.5.6 + resolution: "mkdirp@npm:0.5.6" + dependencies: + minimist: ^1.2.6 + bin: + mkdirp: bin/cmd.js + checksum: 0c91b721bb12c3f9af4b77ebf73604baf350e64d80df91754dc509491ae93bf238581e59c7188360cec7cb62fc4100959245a42cfe01834efedc5e9d068376c2 + languageName: node + linkType: hard + "mkdirp@npm:^1.0.3": version: 1.0.4 resolution: "mkdirp@npm:1.0.4" @@ -17102,7 +17194,7 @@ __metadata: languageName: node linkType: hard -"opener@npm:^1.5.2": +"opener@npm:^1.5.1, opener@npm:^1.5.2": version: 1.5.2 resolution: "opener@npm:1.5.2" bin: @@ -17609,6 +17701,7 @@ __metadata: "@nx/js": 16.8.1 "@nx/linter": 16.8.1 "@nx/nest": 16.8.1 + "@nx/web": 16.8.1 "@nx/workspace": 16.8.1 "@nxext/stencil": ^16.3.0 "@schematics/angular": 16.2.2 @@ -17667,6 +17760,7 @@ __metadata: jest-jasmine2: 29.4.3 jest-preset-angular: 13.1.1 jetifier: ^2.0.0 + jsonc-eslint-parser: ^2.1.0 leaflet: ^1.9.3 lint-staged: ^13.2.1 lottie-web: ^5.10.2 @@ -17803,6 +17897,17 @@ __metadata: languageName: node linkType: hard +"portfinder@npm:^1.0.28": + version: 1.0.32 + resolution: "portfinder@npm:1.0.32" + dependencies: + async: ^2.6.4 + debug: ^3.2.7 + mkdirp: ^0.5.6 + checksum: 116b4aed1b9e16f6d5503823d966d9ffd41b1c2339e27f54c06cd2f3015a9d8ef53e2a53b57bc0a25af0885977b692007353aa28f9a0a98a44335cb50487240d + languageName: node + linkType: hard + "postcss-calc@npm:^9.0.0": version: 9.0.1 resolution: "postcss-calc@npm:9.0.1" @@ -18729,6 +18834,15 @@ __metadata: languageName: node linkType: hard +"qs@npm:^6.4.0": + version: 6.11.2 + resolution: "qs@npm:6.11.2" + dependencies: + side-channel: ^1.0.4 + checksum: e812f3c590b2262548647d62f1637b6989cc56656dc960b893fe2098d96e1bd633f36576f4cd7564dfbff9db42e17775884db96d846bebe4f37420d073ecdc0b + languageName: node + linkType: hard + "querystringify@npm:^2.1.1": version: 2.2.0 resolution: "querystringify@npm:2.2.0" @@ -19519,6 +19633,13 @@ __metadata: languageName: node linkType: hard +"secure-compare@npm:3.0.1": + version: 3.0.1 + resolution: "secure-compare@npm:3.0.1" + checksum: 0a8d8d3e54d5772d2cf1c02325f01fc7366d0bd33f964a08a84fe3ee5f34d46435a6ae729c1d239c750e160ef9b58c764d3efb945a1d07faf47978a8e4161594 + languageName: node + linkType: hard + "select-hose@npm:^2.0.0": version: 2.0.0 resolution: "select-hose@npm:2.0.0" @@ -21183,6 +21304,15 @@ __metadata: languageName: node linkType: hard +"union@npm:~0.5.0": + version: 0.5.0 + resolution: "union@npm:0.5.0" + dependencies: + qs: ^6.4.0 + checksum: 021530d02363fb7470ce45d4cb06ae28a97d5a245666e6d0fca6bab0673bea8c7988e7d2f8046acfbab120908cedcb099ca216b357d4483bcd96518b39101be0 + languageName: node + linkType: hard + "unique-filename@npm:^3.0.0": version: 3.0.0 resolution: "unique-filename@npm:3.0.0" @@ -21275,6 +21405,13 @@ __metadata: languageName: node linkType: hard +"url-join@npm:^4.0.1": + version: 4.0.1 + resolution: "url-join@npm:4.0.1" + checksum: f74e868bf25dbc8be6a8d7237d4c36bb5b6c62c72e594d5ab1347fe91d6af7ccd9eb5d621e30152e4da45c2e9a26bec21390e911ab54a62d4d82e76028374ee5 + languageName: node + linkType: hard + "url-parse@npm:^1.5.3": version: 1.5.10 resolution: "url-parse@npm:1.5.10"