♻️ Remove Nx and rely on pnpm monorepo features

This commit is contained in:
Juanfran 2026-02-04 13:16:07 +01:00
parent 34c99401ac
commit 4351a7abca
75 changed files with 2114 additions and 8676 deletions

1
plugins/.npmrc Normal file
View File

@ -0,0 +1 @@
ignore-workspace-root-check=true

View File

@ -1,7 +1,3 @@
{ {
"recommendations": [ "recommendations": ["esbenp.prettier-vscode", "dbaeumer.vscode-eslint"]
"nrwl.angular-console",
"prettier.prettier-vscode",
"dbaeumer.vscode-eslint"
]
} }

490
plugins/angular.json Normal file
View File

@ -0,0 +1,490 @@
{
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
"version": 1,
"newProjectRoot": "apps",
"projects": {
"contrast-plugin": {
"projectType": "application",
"root": "apps/contrast-plugin",
"sourceRoot": "apps/contrast-plugin/src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:application",
"options": {
"outputPath": "dist/apps/contrast-plugin",
"index": "apps/contrast-plugin/src/index.html",
"browser": "apps/contrast-plugin/src/main.ts",
"polyfills": ["zone.js"],
"tsConfig": "apps/contrast-plugin/tsconfig.app.json",
"assets": [
"apps/contrast-plugin/src/_headers",
"apps/contrast-plugin/src/favicon.ico",
"apps/contrast-plugin/src/assets"
],
"styles": [
"libs/plugins-styles/src/lib/styles.css",
"apps/contrast-plugin/src/styles.css"
],
"scripts": [],
"optimization": {
"scripts": true,
"styles": true,
"fonts": false
}
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"outputHashing": "all"
},
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": { "buildTarget": "contrast-plugin:build:production" },
"development": {
"buildTarget": "contrast-plugin:build:development",
"host": "0.0.0.0",
"port": 4302
}
},
"defaultConfiguration": "development"
}
}
},
"icons-plugin": {
"projectType": "application",
"root": "apps/icons-plugin",
"sourceRoot": "apps/icons-plugin/src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:application",
"options": {
"outputPath": "dist/apps/icons-plugin",
"index": "apps/icons-plugin/src/index.html",
"browser": "apps/icons-plugin/src/main.ts",
"polyfills": ["zone.js"],
"tsConfig": "apps/icons-plugin/tsconfig.app.json",
"assets": [
"apps/icons-plugin/src/_headers",
"apps/icons-plugin/src/favicon.ico",
"apps/icons-plugin/src/assets"
],
"styles": [
"libs/plugins-styles/src/lib/styles.css",
"apps/icons-plugin/src/styles.css"
],
"scripts": [],
"optimization": {
"scripts": true,
"styles": true,
"fonts": false
}
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"outputHashing": "all"
},
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": { "buildTarget": "icons-plugin:build:production" },
"development": {
"buildTarget": "icons-plugin:build:development",
"host": "0.0.0.0",
"port": 4303
}
},
"defaultConfiguration": "development"
}
}
},
"lorem-ipsum-plugin": {
"projectType": "application",
"root": "apps/lorem-ipsum-plugin",
"sourceRoot": "apps/lorem-ipsum-plugin/src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:application",
"options": {
"outputPath": "dist/apps/lorem-ipsum-plugin",
"index": "apps/lorem-ipsum-plugin/src/index.html",
"browser": "apps/lorem-ipsum-plugin/src/main.ts",
"polyfills": ["zone.js"],
"tsConfig": "apps/lorem-ipsum-plugin/tsconfig.app.json",
"assets": [
"apps/lorem-ipsum-plugin/src/_headers",
"apps/lorem-ipsum-plugin/src/favicon.ico",
"apps/lorem-ipsum-plugin/src/assets"
],
"styles": [
"libs/plugins-styles/src/lib/styles.css",
"apps/lorem-ipsum-plugin/src/styles.css"
],
"scripts": [],
"optimization": {
"scripts": true,
"styles": true,
"fonts": false
}
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"outputHashing": "all"
},
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"buildTarget": "lorem-ipsum-plugin:build:production"
},
"development": {
"buildTarget": "lorem-ipsum-plugin:build:development",
"host": "0.0.0.0",
"port": 4304
}
},
"defaultConfiguration": "development"
}
}
},
"table-plugin": {
"projectType": "application",
"root": "apps/table-plugin",
"sourceRoot": "apps/table-plugin/src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:application",
"options": {
"outputPath": "dist/apps/table-plugin",
"index": "apps/table-plugin/src/index.html",
"browser": "apps/table-plugin/src/main.ts",
"polyfills": ["zone.js"],
"tsConfig": "apps/table-plugin/tsconfig.app.json",
"assets": [
"apps/table-plugin/src/_headers",
"apps/table-plugin/src/favicon.ico",
"apps/table-plugin/src/assets"
],
"styles": [
"libs/plugins-styles/src/lib/styles.css",
"apps/table-plugin/src/styles.css"
],
"scripts": [],
"optimization": {
"scripts": true,
"styles": true,
"fonts": false
}
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"outputHashing": "all"
},
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": { "buildTarget": "table-plugin:build:production" },
"development": {
"buildTarget": "table-plugin:build:development",
"host": "0.0.0.0",
"port": 4306
}
},
"defaultConfiguration": "development"
}
}
},
"rename-layers-plugin": {
"projectType": "application",
"root": "apps/rename-layers-plugin",
"sourceRoot": "apps/rename-layers-plugin/src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:application",
"options": {
"outputPath": "dist/apps/rename-layers-plugin",
"index": "apps/rename-layers-plugin/src/index.html",
"browser": "apps/rename-layers-plugin/src/main.ts",
"polyfills": ["zone.js"],
"tsConfig": "apps/rename-layers-plugin/tsconfig.app.json",
"assets": [
"apps/rename-layers-plugin/src/_headers",
"apps/rename-layers-plugin/src/favicon.ico",
"apps/rename-layers-plugin/src/assets"
],
"styles": [
"libs/plugins-styles/src/lib/styles.css",
"apps/rename-layers-plugin/src/styles.css"
],
"scripts": [],
"optimization": {
"scripts": true,
"styles": true,
"fonts": false
}
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"outputHashing": "all"
},
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"buildTarget": "rename-layers-plugin:build:production"
},
"development": {
"buildTarget": "rename-layers-plugin:build:development",
"host": "0.0.0.0",
"port": 4307
}
},
"defaultConfiguration": "development"
}
}
},
"colors-to-tokens-plugin": {
"projectType": "application",
"root": "apps/colors-to-tokens-plugin",
"sourceRoot": "apps/colors-to-tokens-plugin/src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:application",
"options": {
"outputPath": "dist/apps/colors-to-tokens-plugin",
"index": "apps/colors-to-tokens-plugin/src/index.html",
"browser": "apps/colors-to-tokens-plugin/src/main.ts",
"polyfills": ["zone.js"],
"tsConfig": "apps/colors-to-tokens-plugin/tsconfig.app.json",
"assets": [
"apps/colors-to-tokens-plugin/src/_headers",
"apps/colors-to-tokens-plugin/src/favicon.ico",
"apps/colors-to-tokens-plugin/src/assets"
],
"styles": [
"libs/plugins-styles/src/lib/styles.css",
"apps/colors-to-tokens-plugin/src/styles.css"
],
"scripts": [],
"optimization": {
"scripts": true,
"styles": true,
"fonts": false
}
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"outputHashing": "all"
},
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"buildTarget": "colors-to-tokens-plugin:build:production"
},
"development": {
"buildTarget": "colors-to-tokens-plugin:build:development",
"host": "0.0.0.0",
"port": 4308
}
},
"defaultConfiguration": "development"
}
}
},
"poc-state-plugin": {
"projectType": "application",
"root": "apps/poc-state-plugin",
"sourceRoot": "apps/poc-state-plugin/src",
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:application",
"options": {
"outputPath": "dist/apps/poc-state-plugin",
"index": "apps/poc-state-plugin/src/index.html",
"browser": "apps/poc-state-plugin/src/main.ts",
"polyfills": ["zone.js"],
"tsConfig": "apps/poc-state-plugin/tsconfig.app.json",
"assets": [
"apps/poc-state-plugin/src/favicon.ico",
"apps/poc-state-plugin/src/assets"
],
"styles": [
"libs/plugins-styles/src/lib/styles.css",
"apps/poc-state-plugin/src/styles.css"
],
"scripts": [],
"optimization": {
"scripts": true,
"styles": true,
"fonts": false
}
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"outputHashing": "all"
},
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
}
},
"defaultConfiguration": "production"
},
"serve": {
"builder": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"buildTarget": "poc-state-plugin:build:production"
},
"development": {
"buildTarget": "poc-state-plugin:build:development",
"host": "0.0.0.0",
"port": 4309
}
},
"defaultConfiguration": "development"
}
}
}
}
}

View File

@ -1,46 +1,33 @@
import baseConfig from '../../eslint.config.js'; import baseConfig from '../../eslint.config.js';
import { compat } from '../../eslint.base.config.js'; import angular from '@angular-eslint/eslint-plugin';
import angularTemplate from '@angular-eslint/eslint-plugin-template';
import angularTemplateParser from '@angular-eslint/template-parser';
export default [ export default [
...baseConfig, ...baseConfig,
...compat
.config({
extends: [
'plugin:@nx/angular',
'plugin:@angular-eslint/template/process-inline-templates',
],
})
.map((config) => ({
...config,
files: ['**/*.ts'],
rules: {
'@angular-eslint/directive-selector': [
'error',
{
type: 'attribute',
prefix: 'app',
style: 'camelCase',
},
],
'@angular-eslint/component-selector': [
'error',
{
type: 'element',
prefix: 'app',
style: 'kebab-case',
},
],
},
})),
...compat
.config({ extends: ['plugin:@nx/angular-template'] })
.map((config) => ({
...config,
files: ['**/*.html'],
rules: {},
})),
{ ignores: ['**/assets/*.js'] },
{ {
files: ['**/*.ts'],
plugins: {
'@angular-eslint': angular,
},
rules: {
'@angular-eslint/directive-selector': [
'error',
{
type: 'attribute',
prefix: 'app',
style: 'camelCase',
},
],
'@angular-eslint/component-selector': [
'error',
{
type: 'element',
prefix: 'app',
style: 'kebab-case',
},
],
},
languageOptions: { languageOptions: {
parserOptions: { parserOptions: {
project: './tsconfig.*?.json', project: './tsconfig.*?.json',
@ -48,4 +35,16 @@ export default [
}, },
}, },
}, },
{
files: ['**/*.html'],
plugins: {
'@angular-eslint/template': angularTemplate,
},
languageOptions: {
parser: angularTemplateParser,
},
processor: '@angular-eslint/template/extract-inline-html',
rules: {},
},
{ ignores: ['**/assets/*.js'] },
]; ];

View File

@ -0,0 +1,13 @@
{
"name": "colors-to-tokens-plugin",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"build": "ng build colors-to-tokens-plugin",
"build:dev": "ng build colors-to-tokens-plugin --configuration development",
"serve": "ng serve colors-to-tokens-plugin",
"lint": "eslint .",
"test": "vitest"
}
}

View File

@ -1,81 +0,0 @@
{
"name": "colors-to-tokens-plugin",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"prefix": "app",
"sourceRoot": "apps/colors-to-tokens-plugin/src",
"tags": ["type:plugin"],
"targets": {
"build": {
"executor": "@angular-devkit/build-angular:application",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/apps/colors-to-tokens-plugin",
"index": "apps/colors-to-tokens-plugin/src/index.html",
"browser": "apps/colors-to-tokens-plugin/src/main.ts",
"polyfills": ["zone.js"],
"tsConfig": "apps/colors-to-tokens-plugin/tsconfig.app.json",
"assets": [
"apps/colors-to-tokens-plugin/src/_headers",
"apps/colors-to-tokens-plugin/src/favicon.ico",
"apps/colors-to-tokens-plugin/src/assets"
],
"styles": [
"libs/plugins-styles/src/lib/styles.css",
"apps/colors-to-tokens-plugin/src/styles.css"
],
"scripts": [],
"optimization": {
"scripts": true,
"styles": true,
"fonts": false
}
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"outputHashing": "all"
},
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
}
},
"defaultConfiguration": "production",
"dependsOn": ["buildPlugin"]
},
"serve": {
"executor": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"buildTarget": "colors-to-tokens-plugin:build:production"
},
"development": {
"buildTarget": "colors-to-tokens-plugin:build:development",
"host": "0.0.0.0",
"port": 4308
}
},
"defaultConfiguration": "development",
"continuous": true
},
"extract-i18n": {
"executor": "@angular-devkit/build-angular:extract-i18n",
"options": {
"buildTarget": "colors-to-tokens-plugin:build"
}
}
}
}

View File

@ -41,7 +41,7 @@ penpot.ui.onMessage<PluginUIEvent>((message) => {
function resize(width: number, height: number) { function resize(width: number, height: number) {
if ('resize' in penpot.ui) { if ('resize' in penpot.ui) {
(penpot as any).ui.resize(width, height); penpot.ui.resize(width, height);
} }
} }

View File

@ -1,15 +1,11 @@
/// <reference types='vitest' /> /// <reference types="vitest/config" />
import { defineConfig } from 'vite'; import { defineConfig } from 'vite';
export default defineConfig({ export default defineConfig({
root: __dirname, root: __dirname,
cacheDir: '../node_modules/.vite/colors-to-tokens-plugin',
test: { test: {
watch: false, watch: false,
globals: true, globals: true,
cache: {
dir: '../node_modules/.vitest',
},
environment: 'jsdom', environment: 'jsdom',
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
reporters: ['default'], reporters: ['default'],

View File

@ -1,46 +1,33 @@
import baseConfig from '../../eslint.config.js'; import baseConfig from '../../eslint.config.js';
import { compat } from '../../eslint.base.config.js'; import angular from '@angular-eslint/eslint-plugin';
import angularTemplate from '@angular-eslint/eslint-plugin-template';
import angularTemplateParser from '@angular-eslint/template-parser';
export default [ export default [
...baseConfig, ...baseConfig,
...compat
.config({
extends: [
'plugin:@nx/angular',
'plugin:@angular-eslint/template/process-inline-templates',
],
})
.map((config) => ({
...config,
files: ['**/*.ts'],
rules: {
'@angular-eslint/directive-selector': [
'error',
{
type: 'attribute',
prefix: 'app',
style: 'camelCase',
},
],
'@angular-eslint/component-selector': [
'error',
{
type: 'element',
prefix: 'app',
style: 'kebab-case',
},
],
},
})),
...compat
.config({ extends: ['plugin:@nx/angular-template'] })
.map((config) => ({
...config,
files: ['**/*.html'],
rules: {},
})),
{ ignores: ['**/assets/*.js'] },
{ {
files: ['**/*.ts'],
plugins: {
'@angular-eslint': angular,
},
rules: {
'@angular-eslint/directive-selector': [
'error',
{
type: 'attribute',
prefix: 'app',
style: 'camelCase',
},
],
'@angular-eslint/component-selector': [
'error',
{
type: 'element',
prefix: 'app',
style: 'kebab-case',
},
],
},
languageOptions: { languageOptions: {
parserOptions: { parserOptions: {
project: './tsconfig.*?.json', project: './tsconfig.*?.json',
@ -48,4 +35,16 @@ export default [
}, },
}, },
}, },
{
files: ['**/*.html'],
plugins: {
'@angular-eslint/template': angularTemplate,
},
languageOptions: {
parser: angularTemplateParser,
},
processor: '@angular-eslint/template/extract-inline-html',
rules: {},
},
{ ignores: ['**/assets/*.js'] },
]; ];

View File

@ -0,0 +1,13 @@
{
"name": "contrast-plugin",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"build": "ng build contrast-plugin",
"build:dev": "ng build contrast-plugin --configuration development",
"serve": "ng serve contrast-plugin",
"lint": "eslint .",
"test": "vitest"
}
}

View File

@ -1,81 +0,0 @@
{
"name": "contrast-plugin",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"prefix": "app",
"sourceRoot": "apps/contrast-plugin/src",
"tags": ["type:plugin"],
"targets": {
"build": {
"executor": "@angular-devkit/build-angular:application",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/apps/contrast-plugin",
"index": "apps/contrast-plugin/src/index.html",
"browser": "apps/contrast-plugin/src/main.ts",
"polyfills": ["zone.js"],
"tsConfig": "apps/contrast-plugin/tsconfig.app.json",
"assets": [
"apps/contrast-plugin/src/_headers",
"apps/contrast-plugin/src/favicon.ico",
"apps/contrast-plugin/src/assets"
],
"styles": [
"libs/plugins-styles/src/lib/styles.css",
"apps/contrast-plugin/src/styles.css"
],
"scripts": [],
"optimization": {
"scripts": true,
"styles": true,
"fonts": false
}
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"outputHashing": "all"
},
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
}
},
"defaultConfiguration": "production",
"dependsOn": ["buildPlugin"]
},
"serve": {
"executor": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"buildTarget": "contrast-plugin:build:production"
},
"development": {
"buildTarget": "contrast-plugin:build:development",
"host": "0.0.0.0",
"port": 4302
}
},
"defaultConfiguration": "development",
"continuous": true
},
"extract-i18n": {
"executor": "@angular-devkit/build-angular:extract-i18n",
"options": {
"buildTarget": "contrast-plugin:build"
}
}
}
}

View File

@ -1,14 +1,10 @@
/// <reference types='vitest' /> /// <reference types="vitest/config" />
import { defineConfig } from 'vite'; import { defineConfig } from 'vite';
export default defineConfig({ export default defineConfig({
root: __dirname, root: __dirname,
cacheDir: '../node_modules/.vite/contrast-plugin',
test: { test: {
globals: true, globals: true,
cache: {
dir: '../node_modules/.vitest',
},
environment: 'jsdom', environment: 'jsdom',
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
reporters: ['default'], reporters: ['default'],

View File

@ -1,3 +0,0 @@
{
"presets": ["@nx/js/babel"]
}

View File

@ -1,8 +0,0 @@
{
"jsc": {
"parser": {
"syntax": "typescript"
},
"target": "es2016"
}
}

View File

@ -0,0 +1,14 @@
{
"name": "create-palette-plugin",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"build:watch": "vite build --watch --mode development",
"preview": "vite preview",
"lint": "eslint .",
"test": "vitest"
}
}

View File

@ -1,8 +0,0 @@
{
"name": "create-palette-plugin",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"sourceRoot": "apps/create-palette-plugin/src",
"tags": ["type:plugin"],
"targets": {}
}

View File

@ -1,12 +1,9 @@
/// <reference types='vitest' /> /// <reference types="vitest/config" />
import { defineConfig } from 'vite'; import { defineConfig } from 'vite';
import tsconfigPaths from 'vite-tsconfig-paths';
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
export default defineConfig({ export default defineConfig({
root: __dirname, root: __dirname,
cacheDir: '../../node_modules/.vite/apps/create-palette-plugin',
server: { server: {
port: 4305, port: 4305,
host: '0.0.0.0', host: '0.0.0.0',
@ -16,14 +13,7 @@ export default defineConfig({
port: 4305, port: 4305,
host: '0.0.0.0', host: '0.0.0.0',
}, },
plugins: [tsconfigPaths()],
plugins: [nxViteTsPaths()],
// Uncomment this if you are using workers.
// worker: {
// plugins: [ nxViteTsPaths() ],
// },
build: { build: {
outDir: '../../dist/apps/create-palette-plugin', outDir: '../../dist/apps/create-palette-plugin',
reportCompressedSize: true, reportCompressedSize: true,
@ -40,12 +30,8 @@ export default defineConfig({
}, },
}, },
}, },
test: { test: {
globals: true, globals: true,
cache: {
dir: '../../node_modules/.vitest',
},
environment: 'jsdom', environment: 'jsdom',
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],

View File

@ -7,7 +7,7 @@ export default [
{ {
languageOptions: { languageOptions: {
parser: typescriptEslintParser, parser: typescriptEslintParser,
parserOptions: { project: './apps/e2e/tsconfig.json' }, parserOptions: { project: './tsconfig.json' },
}, },
}, },
{ {

View File

@ -0,0 +1,10 @@
{
"name": "e2e",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"test": "vitest",
"lint": "eslint ."
}
}

View File

@ -1,8 +0,0 @@
{
"name": "e2e",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"implicitDependencies": [],
"tags": ["type:e2e"],
"targets": {}
}

View File

@ -107,6 +107,7 @@ export async function Agent() {
console.log('Running plugin code...'); console.log('Running plugin code...');
await page.evaluate((testingPlugin) => { await page.evaluate((testingPlugin) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
(globalThis as any).ɵloadPlugin({ (globalThis as any).ɵloadPlugin({
pluginId: 'TEST', pluginId: 'TEST',
name: 'Test', name: 'Test',

View File

@ -1,16 +1,12 @@
/// <reference types='vitest' /> /// <reference types="vitest/config" />
import { defineConfig } from 'vite'; import { defineConfig } from 'vite';
export default defineConfig({ export default defineConfig({
root: __dirname, root: __dirname,
cacheDir: '../../node_modules/.vite/e2e',
test: { test: {
testTimeout: 20000, testTimeout: 20000,
watch: false, watch: false,
globals: true, globals: true,
cache: {
dir: '../node_modules/.vitest',
},
environment: 'happy-dom', environment: 'happy-dom',
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
reporters: ['default'], reporters: ['default'],

View File

@ -1,3 +0,0 @@
{
"presets": ["@nx/js/babel"]
}

View File

@ -1,8 +0,0 @@
{
"jsc": {
"parser": {
"syntax": "typescript"
},
"target": "es2016"
}
}

View File

@ -0,0 +1,13 @@
{
"name": "example-styles",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"build:watch": "vite build --watch --mode development",
"preview": "vite preview",
"lint": "eslint ."
}
}

View File

@ -1,8 +0,0 @@
{
"name": "example-styles",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"sourceRoot": "apps/example-styles/src",
"tags": ["type:app"],
"targets": {}
}

View File

@ -25,7 +25,8 @@ export class AppElement extends HTMLElement {
el.remove(); el.remove();
}); });
(window as any).hljs.highlightAll(); // eslint-disable-next-line @typescript-eslint/no-explicit-any
(window as any).hljs?.highlightAll();
} }
getIndentationSize(str: string) { getIndentationSize(str: string) {

View File

@ -1,12 +1,9 @@
/// <reference types='vitest' /> /// <reference types="vitest/config" />
import { defineConfig } from 'vite'; import { defineConfig } from 'vite';
import tsconfigPaths from 'vite-tsconfig-paths';
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin';
export default defineConfig({ export default defineConfig({
root: __dirname, root: __dirname,
cacheDir: '../../node_modules/.vite/apps/example-styles',
server: { server: {
port: 4201, port: 4201,
host: '0.0.0.0', host: '0.0.0.0',
@ -17,12 +14,7 @@ export default defineConfig({
host: '0.0.0.0', host: '0.0.0.0',
}, },
plugins: [nxViteTsPaths()], plugins: [tsconfigPaths()],
// Uncomment this if you are using workers.
// worker: {
// plugins: [ nxViteTsPaths() ],
// },
build: { build: {
outDir: '../../dist/apps/example-styles', outDir: '../../dist/apps/example-styles',
@ -34,9 +26,6 @@ export default defineConfig({
test: { test: {
globals: true, globals: true,
cache: {
dir: '../../node_modules/.vitest',
},
environment: 'jsdom', environment: 'jsdom',
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],

View File

@ -1,46 +1,33 @@
import baseConfig from '../../eslint.config.js'; import baseConfig from '../../eslint.config.js';
import { compat } from '../../eslint.base.config.js'; import angular from '@angular-eslint/eslint-plugin';
import angularTemplate from '@angular-eslint/eslint-plugin-template';
import angularTemplateParser from '@angular-eslint/template-parser';
export default [ export default [
...baseConfig, ...baseConfig,
...compat
.config({
extends: [
'plugin:@nx/angular',
'plugin:@angular-eslint/template/process-inline-templates',
],
})
.map((config) => ({
...config,
files: ['**/*.ts'],
rules: {
'@angular-eslint/directive-selector': [
'error',
{
type: 'attribute',
prefix: 'app',
style: 'camelCase',
},
],
'@angular-eslint/component-selector': [
'error',
{
type: 'element',
prefix: 'app',
style: 'kebab-case',
},
],
},
})),
...compat
.config({ extends: ['plugin:@nx/angular-template'] })
.map((config) => ({
...config,
files: ['**/*.html'],
rules: {},
})),
{ ignores: ['**/assets/*.js'] },
{ {
files: ['**/*.ts'],
plugins: {
'@angular-eslint': angular,
},
rules: {
'@angular-eslint/directive-selector': [
'error',
{
type: 'attribute',
prefix: 'app',
style: 'camelCase',
},
],
'@angular-eslint/component-selector': [
'error',
{
type: 'element',
prefix: 'app',
style: 'kebab-case',
},
],
},
languageOptions: { languageOptions: {
parserOptions: { parserOptions: {
project: './tsconfig.*?.json', project: './tsconfig.*?.json',
@ -48,4 +35,16 @@ export default [
}, },
}, },
}, },
{
files: ['**/*.html'],
plugins: {
'@angular-eslint/template': angularTemplate,
},
languageOptions: {
parser: angularTemplateParser,
},
processor: '@angular-eslint/template/extract-inline-html',
rules: {},
},
{ ignores: ['**/assets/*.js'] },
]; ];

View File

@ -0,0 +1,13 @@
{
"name": "icons-plugin",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"build": "ng build icons-plugin",
"build:dev": "ng build icons-plugin --configuration development",
"serve": "ng serve icons-plugin",
"lint": "eslint .",
"test": "vitest"
}
}

View File

@ -1,81 +0,0 @@
{
"name": "icons-plugin",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"prefix": "app",
"sourceRoot": "apps/icons-plugin/src",
"tags": ["type:plugin"],
"targets": {
"build": {
"executor": "@angular-devkit/build-angular:application",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/apps/icons-plugin",
"index": "apps/icons-plugin/src/index.html",
"browser": "apps/icons-plugin/src/main.ts",
"polyfills": ["zone.js"],
"tsConfig": "apps/icons-plugin/tsconfig.app.json",
"assets": [
"apps/icons-plugin/src/_headers",
"apps/icons-plugin/src/favicon.ico",
"apps/icons-plugin/src/assets"
],
"styles": [
"libs/plugins-styles/src/lib/styles.css",
"apps/icons-plugin/src/styles.css"
],
"optimization": {
"scripts": true,
"styles": true,
"fonts": false
},
"scripts": []
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"outputHashing": "all"
},
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
}
},
"defaultConfiguration": "production",
"dependsOn": ["buildPlugin"]
},
"serve": {
"executor": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"buildTarget": "icons-plugin:build:production"
},
"development": {
"buildTarget": "icons-plugin:build:development",
"host": "0.0.0.0",
"port": 4303
}
},
"defaultConfiguration": "development",
"continuous": true
},
"extract-i18n": {
"executor": "@angular-devkit/build-angular:extract-i18n",
"options": {
"buildTarget": "icons-plugin:build"
}
}
}
}

View File

@ -1,7 +1,6 @@
import { Component, inject, signal } from '@angular/core'; import { Component, inject, signal } from '@angular/core';
import { ActivatedRoute, RouterModule } from '@angular/router'; import { ActivatedRoute, RouterModule } from '@angular/router';
import { FeatherIconNames, icons } from 'feather-icons'; import { FeatherIconNames, icons } from 'feather-icons';
import { SafeHtmlPipe } from './pipes/safe-html.pipe';
import { IconButtonComponent } from './components/icon-button/icon-button.component'; import { IconButtonComponent } from './components/icon-button/icon-button.component';
import { IconSearchComponent } from './components/icon-search/icon-search.component'; import { IconSearchComponent } from './components/icon-search/icon-search.component';
import { toSignal } from '@angular/core/rxjs-interop'; import { toSignal } from '@angular/core/rxjs-interop';
@ -10,12 +9,7 @@ import { PluginMessageEvent } from '../model';
@Component({ @Component({
selector: 'app-root', selector: 'app-root',
imports: [ imports: [RouterModule, IconButtonComponent, IconSearchComponent],
RouterModule,
SafeHtmlPipe,
IconButtonComponent,
IconSearchComponent,
],
styleUrl: './app.component.css', styleUrl: './app.component.css',
template: `<div class="icons-plugin"> template: `<div class="icons-plugin">
<div class="icons-search"> <div class="icons-search">

View File

@ -1,46 +1,33 @@
import baseConfig from '../../eslint.config.js'; import baseConfig from '../../eslint.config.js';
import { compat } from '../../eslint.base.config.js'; import angular from '@angular-eslint/eslint-plugin';
import angularTemplate from '@angular-eslint/eslint-plugin-template';
import angularTemplateParser from '@angular-eslint/template-parser';
export default [ export default [
...baseConfig, ...baseConfig,
...compat
.config({
extends: [
'plugin:@nx/angular',
'plugin:@angular-eslint/template/process-inline-templates',
],
})
.map((config) => ({
...config,
files: ['**/*.ts'],
rules: {
'@angular-eslint/directive-selector': [
'error',
{
type: 'attribute',
prefix: 'app',
style: 'camelCase',
},
],
'@angular-eslint/component-selector': [
'error',
{
type: 'element',
prefix: 'app',
style: 'kebab-case',
},
],
},
})),
...compat
.config({ extends: ['plugin:@nx/angular-template'] })
.map((config) => ({
...config,
files: ['**/*.html'],
rules: {},
})),
{ ignores: ['**/assets/*.js'] },
{ {
files: ['**/*.ts'],
plugins: {
'@angular-eslint': angular,
},
rules: {
'@angular-eslint/directive-selector': [
'error',
{
type: 'attribute',
prefix: 'app',
style: 'camelCase',
},
],
'@angular-eslint/component-selector': [
'error',
{
type: 'element',
prefix: 'app',
style: 'kebab-case',
},
],
},
languageOptions: { languageOptions: {
parserOptions: { parserOptions: {
project: './tsconfig.*?.json', project: './tsconfig.*?.json',
@ -48,4 +35,16 @@ export default [
}, },
}, },
}, },
{
files: ['**/*.html'],
plugins: {
'@angular-eslint/template': angularTemplate,
},
languageOptions: {
parser: angularTemplateParser,
},
processor: '@angular-eslint/template/extract-inline-html',
rules: {},
},
{ ignores: ['**/assets/*.js'] },
]; ];

View File

@ -0,0 +1,13 @@
{
"name": "lorem-ipsum-plugin",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"build": "ng build lorem-ipsum-plugin",
"build:dev": "ng build lorem-ipsum-plugin --configuration development",
"serve": "ng serve lorem-ipsum-plugin",
"lint": "eslint .",
"test": "vitest"
}
}

View File

@ -1,81 +0,0 @@
{
"name": "lorem-ipsum-plugin",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"prefix": "app",
"sourceRoot": "apps/lorem-ipsum-plugin/src",
"tags": ["type:plugin"],
"targets": {
"build": {
"executor": "@angular-devkit/build-angular:application",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/apps/lorem-ipsum-plugin",
"index": "apps/lorem-ipsum-plugin/src/index.html",
"browser": "apps/lorem-ipsum-plugin/src/main.ts",
"polyfills": ["zone.js"],
"tsConfig": "apps/lorem-ipsum-plugin/tsconfig.app.json",
"assets": [
"apps/lorem-ipsum-plugin/src/_headers",
"apps/lorem-ipsum-plugin/src/favicon.ico",
"apps/lorem-ipsum-plugin/src/assets"
],
"styles": [
"libs/plugins-styles/src/lib/styles.css",
"apps/lorem-ipsum-plugin/src/styles.css"
],
"scripts": [],
"optimization": {
"scripts": true,
"styles": true,
"fonts": false
}
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"outputHashing": "all"
},
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
}
},
"defaultConfiguration": "production",
"dependsOn": ["buildPlugin"]
},
"serve": {
"executor": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"buildTarget": "lorem-ipsum-plugin:build:production"
},
"development": {
"buildTarget": "lorem-ipsum-plugin:build:development",
"host": "0.0.0.0",
"port": 4304
}
},
"defaultConfiguration": "development",
"continuous": true
},
"extract-i18n": {
"executor": "@angular-devkit/build-angular:extract-i18n",
"options": {
"buildTarget": "lorem-ipsum-plugin:build"
}
}
}
}

View File

@ -101,7 +101,7 @@ export function generateCharacters(count: number, startWithLorem = true) {
} }
export function generateWords(count: number, startWithLorem = true) { export function generateWords(count: number, startWithLorem = true) {
let words = []; const words = [];
if (startWithLorem) { if (startWithLorem) {
words.push(...lorem.split(' ').slice(0, count)); words.push(...lorem.split(' ').slice(0, count));
@ -115,9 +115,9 @@ export function generateWords(count: number, startWithLorem = true) {
} }
export function generateSentences(count: number, startWithLorem = true) { export function generateSentences(count: number, startWithLorem = true) {
let sentences = []; const sentences = [];
for (let i = 0; i < count; i++) { for (let i = 0; i < count; i++) {
let sentenceLength = Math.floor(Math.random() * 10) + 3; // between 3 and 12 words per sentence const sentenceLength = Math.floor(Math.random() * 10) + 3; // between 3 and 12 words per sentence
let sentence = generateWords(sentenceLength, false); let sentence = generateWords(sentenceLength, false);
if (startWithLorem && i === 0) { if (startWithLorem && i === 0) {
@ -131,9 +131,9 @@ export function generateSentences(count: number, startWithLorem = true) {
} }
export function generateParagraphs(count: number, startWithLorem = true) { export function generateParagraphs(count: number, startWithLorem = true) {
let paragraphs = []; const paragraphs = [];
for (let i = 0; i < count; i++) { for (let i = 0; i < count; i++) {
let paragraphLength = Math.floor(Math.random() * 5) + 3; // between 3 and 7 sentences per paragraph const paragraphLength = Math.floor(Math.random() * 5) + 3; // between 3 and 7 sentences per paragraph
paragraphs.push( paragraphs.push(
generateSentences(paragraphLength, startWithLorem && i === 0), generateSentences(paragraphLength, startWithLorem && i === 0),
); );

View File

@ -1,15 +1,11 @@
/// <reference types='vitest' /> /// <reference types="vitest/config" />
import { defineConfig } from 'vite'; import { defineConfig } from 'vite';
export default defineConfig({ export default defineConfig({
root: __dirname, root: __dirname,
cacheDir: '../node_modules/.vite/lorem-ipsum-plugin',
test: { test: {
watch: false, watch: false,
globals: true, globals: true,
cache: {
dir: '../node_modules/.vitest',
},
environment: 'jsdom', environment: 'jsdom',
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],
reporters: ['default'], reporters: ['default'],

View File

@ -1,46 +1,33 @@
import baseConfig from '../../eslint.config.js'; import baseConfig from '../../eslint.config.js';
import { compat } from '../../eslint.base.config.js'; import angular from '@angular-eslint/eslint-plugin';
import angularTemplate from '@angular-eslint/eslint-plugin-template';
import angularTemplateParser from '@angular-eslint/template-parser';
export default [ export default [
...baseConfig, ...baseConfig,
...compat
.config({
extends: [
'plugin:@nx/angular',
'plugin:@angular-eslint/template/process-inline-templates',
],
})
.map((config) => ({
...config,
files: ['**/*.ts'],
rules: {
'@angular-eslint/directive-selector': [
'error',
{
type: 'attribute',
prefix: 'app',
style: 'camelCase',
},
],
'@angular-eslint/component-selector': [
'error',
{
type: 'element',
prefix: 'app',
style: 'kebab-case',
},
],
},
})),
...compat
.config({ extends: ['plugin:@nx/angular-template'] })
.map((config) => ({
...config,
files: ['**/*.html'],
rules: {},
})),
{ ignores: ['**/assets/*.js'] },
{ {
files: ['**/*.ts'],
plugins: {
'@angular-eslint': angular,
},
rules: {
'@angular-eslint/directive-selector': [
'error',
{
type: 'attribute',
prefix: 'app',
style: 'camelCase',
},
],
'@angular-eslint/component-selector': [
'error',
{
type: 'element',
prefix: 'app',
style: 'kebab-case',
},
],
},
languageOptions: { languageOptions: {
parserOptions: { parserOptions: {
project: './tsconfig.*?.json', project: './tsconfig.*?.json',
@ -48,4 +35,15 @@ export default [
}, },
}, },
}, },
{
files: ['**/*.html'],
plugins: {
'@angular-eslint/template': angularTemplate,
},
languageOptions: {
parser: angularTemplateParser,
},
rules: {},
},
{ ignores: ['**/assets/*.js'] },
]; ];

View File

@ -0,0 +1,13 @@
{
"name": "poc-state-plugin",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"build": "ng build poc-state-plugin",
"build:dev": "ng build poc-state-plugin --configuration development",
"serve": "ng serve poc-state-plugin",
"lint": "eslint .",
"test": "vitest"
}
}

View File

@ -1,80 +0,0 @@
{
"name": "poc-state-plugin",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"prefix": "app",
"sourceRoot": "apps/poc-state-plugin/src",
"tags": ["type:plugin"],
"targets": {
"build": {
"executor": "@angular-devkit/build-angular:application",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/apps/poc-state-plugin",
"index": "apps/poc-state-plugin/src/index.html",
"browser": "apps/poc-state-plugin/src/main.ts",
"polyfills": ["zone.js"],
"tsConfig": "apps/poc-state-plugin/tsconfig.app.json",
"assets": [
"apps/poc-state-plugin/src/favicon.ico",
"apps/poc-state-plugin/src/assets"
],
"styles": [
"libs/plugins-styles/src/lib/styles.css",
"apps/poc-state-plugin/src/styles.css"
],
"scripts": [],
"optimization": {
"scripts": true,
"styles": true,
"fonts": false
}
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"outputHashing": "all"
},
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
}
},
"defaultConfiguration": "production",
"dependsOn": ["buildPlugin"]
},
"serve": {
"executor": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"buildTarget": "poc-state-plugin:build:production"
},
"development": {
"buildTarget": "poc-state-plugin:build:development",
"port": 4301,
"host": "0.0.0.0"
}
},
"defaultConfiguration": "development",
"continuous": true
},
"extract-i18n": {
"executor": "@angular-devkit/build-angular:extract-i18n",
"options": {
"buildTarget": "poc-state-plugin:build"
}
}
}
}

View File

@ -533,7 +533,7 @@ async function exportSelected() {
const selection = await penpot.selection[0]; const selection = await penpot.selection[0];
if (selection) { if (selection) {
let data = await selection.export({ type: 'png', skipChildren: true }); const data = await selection.export({ type: 'png', skipChildren: true });
penpot.ui.sendMessage({ penpot.ui.sendMessage({
type: 'start-download', type: 'start-download',
name: 'export.png', name: 'export.png',
@ -547,8 +547,8 @@ async function resizeModal() {
} }
async function saveLocalStorage() { async function saveLocalStorage() {
let oldvalue = penpot.localStorage.getItem('test'); const oldvalue = penpot.localStorage.getItem('test');
let newvalue = oldvalue ? parseInt(oldvalue, 10) + 1 : 1; const newvalue = oldvalue ? parseInt(oldvalue, 10) + 1 : 1;
console.log(newvalue); console.log(newvalue);
penpot.localStorage.setItem('test', newvalue); penpot.localStorage.setItem('test', newvalue);
} }

View File

@ -1,46 +1,33 @@
import baseConfig from '../../eslint.config.js'; import baseConfig from '../../eslint.config.js';
import { compat } from '../../eslint.base.config.js'; import angular from '@angular-eslint/eslint-plugin';
import angularTemplate from '@angular-eslint/eslint-plugin-template';
import angularTemplateParser from '@angular-eslint/template-parser';
export default [ export default [
...baseConfig, ...baseConfig,
...compat
.config({
extends: [
'plugin:@nx/angular',
'plugin:@angular-eslint/template/process-inline-templates',
],
})
.map((config) => ({
...config,
files: ['**/*.ts'],
rules: {
'@angular-eslint/directive-selector': [
'error',
{
type: 'attribute',
prefix: 'app',
style: 'camelCase',
},
],
'@angular-eslint/component-selector': [
'error',
{
type: 'element',
prefix: 'app',
style: 'kebab-case',
},
],
},
})),
...compat
.config({ extends: ['plugin:@nx/angular-template'] })
.map((config) => ({
...config,
files: ['**/*.html'],
rules: {},
})),
{ ignores: ['**/assets/*.js'] },
{ {
files: ['**/*.ts'],
plugins: {
'@angular-eslint': angular,
},
rules: {
'@angular-eslint/directive-selector': [
'error',
{
type: 'attribute',
prefix: 'app',
style: 'camelCase',
},
],
'@angular-eslint/component-selector': [
'error',
{
type: 'element',
prefix: 'app',
style: 'kebab-case',
},
],
},
languageOptions: { languageOptions: {
parserOptions: { parserOptions: {
project: './tsconfig.*?.json', project: './tsconfig.*?.json',
@ -48,4 +35,16 @@ export default [
}, },
}, },
}, },
{
files: ['**/*.html'],
plugins: {
'@angular-eslint/template': angularTemplate,
},
languageOptions: {
parser: angularTemplateParser,
},
processor: '@angular-eslint/template/extract-inline-html',
rules: {},
},
{ ignores: ['**/assets/*.js'] },
]; ];

View File

@ -0,0 +1,13 @@
{
"name": "rename-layers-plugin",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"build": "ng build rename-layers-plugin",
"build:dev": "ng build rename-layers-plugin --configuration development",
"serve": "ng serve rename-layers-plugin",
"lint": "eslint .",
"test": "vitest"
}
}

View File

@ -1,81 +0,0 @@
{
"name": "rename-layers-plugin",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"prefix": "app",
"sourceRoot": "apps/rename-layers-plugin/src",
"tags": ["type:plugin"],
"targets": {
"build": {
"executor": "@angular-devkit/build-angular:application",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/apps/rename-layers-plugin",
"index": "apps/rename-layers-plugin/src/index.html",
"browser": "apps/rename-layers-plugin/src/main.ts",
"polyfills": ["zone.js"],
"tsConfig": "apps/rename-layers-plugin/tsconfig.app.json",
"assets": [
"apps/rename-layers-plugin/src/_headers",
"apps/rename-layers-plugin/src/favicon.ico",
"apps/rename-layers-plugin/src/assets"
],
"styles": [
"libs/plugins-styles/src/lib/styles.css",
"apps/rename-layers-plugin/src/styles.css"
],
"optimization": {
"scripts": true,
"styles": true,
"fonts": false
},
"scripts": []
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"outputHashing": "all"
},
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
}
},
"defaultConfiguration": "production",
"dependsOn": ["buildPlugin"]
},
"serve": {
"executor": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"buildTarget": "rename-layers-plugin:build:production"
},
"development": {
"buildTarget": "rename-layers-plugin:build:development",
"host": "0.0.0.0",
"port": 4307
}
},
"defaultConfiguration": "development",
"continuous": true
},
"extract-i18n": {
"executor": "@angular-devkit/build-angular:extract-i18n",
"options": {
"buildTarget": "rename-layers-plugin:build"
}
}
}
}

View File

@ -25,7 +25,6 @@ penpot.ui.onMessage<PluginMessageEvent>((message) => {
}); });
shapesToUpdate?.forEach((shape) => { shapesToUpdate?.forEach((shape) => {
shape.name = shape.name.replace( shape.name = shape.name.replace(
// eslint-disable-next-line
message.content.search, message.content.search,
message.content.replace, message.content.replace,
); );

View File

@ -1,46 +1,33 @@
import baseConfig from '../../eslint.config.js'; import baseConfig from '../../eslint.config.js';
import { compat } from '../../eslint.base.config.js'; import angular from '@angular-eslint/eslint-plugin';
import angularTemplate from '@angular-eslint/eslint-plugin-template';
import angularTemplateParser from '@angular-eslint/template-parser';
export default [ export default [
...baseConfig, ...baseConfig,
...compat
.config({
extends: [
'plugin:@nx/angular',
'plugin:@angular-eslint/template/process-inline-templates',
],
})
.map((config) => ({
...config,
files: ['**/*.ts'],
rules: {
'@angular-eslint/directive-selector': [
'error',
{
type: 'attribute',
prefix: 'app',
style: 'camelCase',
},
],
'@angular-eslint/component-selector': [
'error',
{
type: 'element',
prefix: 'app',
style: 'kebab-case',
},
],
},
})),
...compat
.config({ extends: ['plugin:@nx/angular-template'] })
.map((config) => ({
...config,
files: ['**/*.html'],
rules: {},
})),
{ ignores: ['**/assets/*.js'] },
{ {
files: ['**/*.ts'],
plugins: {
'@angular-eslint': angular,
},
rules: {
'@angular-eslint/directive-selector': [
'error',
{
type: 'attribute',
prefix: 'app',
style: 'camelCase',
},
],
'@angular-eslint/component-selector': [
'error',
{
type: 'element',
prefix: 'app',
style: 'kebab-case',
},
],
},
languageOptions: { languageOptions: {
parserOptions: { parserOptions: {
project: './tsconfig.*?.json', project: './tsconfig.*?.json',
@ -48,4 +35,16 @@ export default [
}, },
}, },
}, },
{
files: ['**/*.html'],
plugins: {
'@angular-eslint/template': angularTemplate,
},
languageOptions: {
parser: angularTemplateParser,
},
processor: '@angular-eslint/template/extract-inline-html',
rules: {},
},
{ ignores: ['**/assets/*.js'] },
]; ];

View File

@ -0,0 +1,13 @@
{
"name": "table-plugin",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"build": "ng build table-plugin",
"build:dev": "ng build table-plugin --configuration development",
"serve": "ng serve table-plugin",
"lint": "eslint .",
"test": "vitest"
}
}

View File

@ -1,80 +0,0 @@
{
"name": "table-plugin",
"$schema": "../../node_modules/nx/schemas/project-schema.json",
"projectType": "application",
"prefix": "app",
"sourceRoot": "apps/table-plugin/src",
"tags": ["type:plugin"],
"targets": {
"build": {
"executor": "@angular-devkit/build-angular:application",
"outputs": ["{options.outputPath}"],
"options": {
"outputPath": "dist/apps/table-plugin",
"index": "apps/table-plugin/src/index.html",
"browser": "apps/table-plugin/src/main.ts",
"polyfills": ["zone.js"],
"tsConfig": "apps/table-plugin/tsconfig.app.json",
"assets": [
"apps/table-plugin/src/_headers",
"apps/table-plugin/src/favicon.ico",
"apps/table-plugin/src/assets"
],
"styles": [
"libs/plugins-styles/src/lib/styles.css",
"apps/table-plugin/src/styles.css"
],
"optimization": {
"scripts": true,
"styles": true,
"fonts": false
},
"scripts": []
},
"configurations": {
"production": {
"budgets": [
{
"type": "initial",
"maximumWarning": "500kb",
"maximumError": "1mb"
},
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
}
],
"outputHashing": "all"
},
"development": {
"optimization": false,
"extractLicenses": false,
"sourceMap": true
}
},
"defaultConfiguration": "production",
"dependsOn": ["buildPlugin"]
},
"serve": {
"executor": "@angular-devkit/build-angular:dev-server",
"configurations": {
"production": {
"buildTarget": "table-plugin:build:production"
},
"development": {
"buildTarget": "table-plugin:build:development",
"port": 4306
}
},
"defaultConfiguration": "development",
"continuous": true
},
"extract-i18n": {
"executor": "@angular-devkit/build-angular:extract-i18n",
"options": {
"buildTarget": "table-plugin:build"
}
}
}
}

View File

@ -69,7 +69,7 @@ export class AppComponent {
target.files[0] && target.files[0] &&
target.files[0].type === 'text/csv' target.files[0].type === 'text/csv'
) { ) {
var reader = new FileReader(); const reader = new FileReader();
reader.readAsText(target.files[0]); reader.readAsText(target.files[0]);
reader.onload = (e) => { reader.onload = (e) => {
this.table = (e?.target?.result as string) this.table = (e?.target?.result as string)

View File

@ -1,986 +0,0 @@
import { Component, ViewEncapsulation } from '@angular/core';
import { CommonModule } from '@angular/common';
@Component({
selector: 'app-nx-welcome',
standalone: true,
imports: [CommonModule],
template: `
<!--
* * * * * * * * * * * * * * * * * * * * * * * * * * * *
This is a starter component and can be deleted.
* * * * * * * * * * * * * * * * * * * * * * * * * * * *
Delete this file and get started with your project!
* * * * * * * * * * * * * * * * * * * * * * * * * * * *
-->
<style>
html {
-webkit-text-size-adjust: 100%;
font-family:
ui-sans-serif,
system-ui,
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
Roboto,
'Helvetica Neue',
Arial,
'Noto Sans',
sans-serif,
'Apple Color Emoji',
'Segoe UI Emoji',
'Segoe UI Symbol',
'Noto Color Emoji';
line-height: 1.5;
tab-size: 4;
scroll-behavior: smooth;
}
body {
font-family: inherit;
line-height: inherit;
margin: 0;
}
h1,
h2,
p,
pre {
margin: 0;
}
*,
::before,
::after {
box-sizing: border-box;
border-width: 0;
border-style: solid;
border-color: currentColor;
}
h1,
h2 {
font-size: inherit;
font-weight: inherit;
}
a {
color: inherit;
text-decoration: inherit;
}
pre {
font-family:
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
'Liberation Mono', 'Courier New', monospace;
}
svg {
display: block;
vertical-align: middle;
}
svg {
shape-rendering: auto;
text-rendering: optimizeLegibility;
}
pre {
background-color: rgba(55, 65, 81, 1);
border-radius: 0.25rem;
color: rgba(229, 231, 235, 1);
font-family:
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas,
'Liberation Mono', 'Courier New', monospace;
overflow: scroll;
padding: 0.5rem 0.75rem;
}
.shadow {
box-shadow:
0 0 #0000,
0 0 #0000,
0 10px 15px -3px rgba(0, 0, 0, 0.1),
0 4px 6px -2px rgba(0, 0, 0, 0.05);
}
.rounded {
border-radius: 1.5rem;
}
.wrapper {
width: 100%;
}
.container {
margin-left: auto;
margin-right: auto;
max-width: 768px;
padding-bottom: 3rem;
padding-left: 1rem;
padding-right: 1rem;
color: rgba(55, 65, 81, 1);
width: 100%;
}
#welcome {
margin-top: 2.5rem;
}
#welcome h1 {
font-size: 3rem;
font-weight: 500;
letter-spacing: -0.025em;
line-height: 1;
}
#welcome span {
display: block;
font-size: 1.875rem;
font-weight: 300;
line-height: 2.25rem;
margin-bottom: 0.5rem;
}
#hero {
align-items: center;
background-color: hsla(214, 62%, 21%, 1);
border: none;
box-sizing: border-box;
color: rgba(55, 65, 81, 1);
display: grid;
grid-template-columns: 1fr;
margin-top: 3.5rem;
}
#hero .text-container {
color: rgba(255, 255, 255, 1);
padding: 3rem 2rem;
}
#hero .text-container h2 {
font-size: 1.5rem;
line-height: 2rem;
position: relative;
}
#hero .text-container h2 svg {
color: hsla(162, 47%, 50%, 1);
height: 2rem;
left: -0.25rem;
position: absolute;
top: 0;
width: 2rem;
}
#hero .text-container h2 span {
margin-left: 2.5rem;
}
#hero .text-container a {
background-color: rgba(255, 255, 255, 1);
border-radius: 0.75rem;
color: rgba(55, 65, 81, 1);
display: inline-block;
margin-top: 1.5rem;
padding: 1rem 2rem;
text-decoration: inherit;
}
#hero .logo-container {
display: none;
justify-content: center;
padding-left: 2rem;
padding-right: 2rem;
}
#hero .logo-container svg {
color: rgba(255, 255, 255, 1);
width: 66.666667%;
}
#middle-content {
align-items: flex-start;
display: grid;
gap: 4rem;
grid-template-columns: 1fr;
margin-top: 3.5rem;
}
#learning-materials {
padding: 2.5rem 2rem;
}
#learning-materials h2 {
font-weight: 500;
font-size: 1.25rem;
letter-spacing: -0.025em;
line-height: 1.75rem;
padding-left: 1rem;
padding-right: 1rem;
}
.list-item-link {
align-items: center;
border-radius: 0.75rem;
display: flex;
margin-top: 1rem;
padding: 1rem;
transition-property:
background-color,
border-color,
color,
fill,
stroke,
opacity,
box-shadow,
transform,
filter,
backdrop-filter,
-webkit-backdrop-filter;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
width: 100%;
}
.list-item-link svg:first-child {
margin-right: 1rem;
height: 1.5rem;
transition-property:
background-color,
border-color,
color,
fill,
stroke,
opacity,
box-shadow,
transform,
filter,
backdrop-filter,
-webkit-backdrop-filter;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
width: 1.5rem;
}
.list-item-link > span {
flex-grow: 1;
font-weight: 400;
transition-property:
background-color,
border-color,
color,
fill,
stroke,
opacity,
box-shadow,
transform,
filter,
backdrop-filter,
-webkit-backdrop-filter;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.list-item-link > span > span {
color: rgba(107, 114, 128, 1);
display: block;
flex-grow: 1;
font-size: 0.75rem;
font-weight: 300;
line-height: 1rem;
transition-property:
background-color,
border-color,
color,
fill,
stroke,
opacity,
box-shadow,
transform,
filter,
backdrop-filter,
-webkit-backdrop-filter;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
.list-item-link svg:last-child {
height: 1rem;
transition-property: all;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
width: 1rem;
}
.list-item-link:hover {
color: rgba(255, 255, 255, 1);
background-color: hsla(162, 47%, 50%, 1);
}
.list-item-link:hover > span {
}
.list-item-link:hover > span > span {
color: rgba(243, 244, 246, 1);
}
.list-item-link:hover svg:last-child {
transform: translateX(0.25rem);
}
#other-links {
}
.button-pill {
padding: 1.5rem 2rem;
margin-bottom: 2rem;
transition-duration: 300ms;
transition-property:
background-color,
border-color,
color,
fill,
stroke,
opacity,
box-shadow,
transform,
filter,
backdrop-filter,
-webkit-backdrop-filter;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
align-items: center;
display: flex;
}
.button-pill svg {
transition-property:
background-color,
border-color,
color,
fill,
stroke,
opacity,
box-shadow,
transform,
filter,
backdrop-filter,
-webkit-backdrop-filter;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
flex-shrink: 0;
width: 3rem;
}
.button-pill > span {
letter-spacing: -0.025em;
font-weight: 400;
font-size: 1.125rem;
line-height: 1.75rem;
padding-left: 1rem;
padding-right: 1rem;
}
.button-pill span span {
display: block;
font-size: 0.875rem;
font-weight: 300;
line-height: 1.25rem;
}
.button-pill:hover svg,
.button-pill:hover {
color: rgba(255, 255, 255, 1) !important;
}
.nx-console:hover {
background-color: rgba(0, 122, 204, 1);
}
.nx-console svg {
color: rgba(0, 122, 204, 1);
}
.nx-console-jetbrains {
margin-top: 2rem;
}
.nx-console-jetbrains:hover {
background-color: rgba(255, 49, 140, 1);
}
.nx-console-jetbrains svg {
color: rgba(255, 49, 140, 1);
}
#nx-repo:hover {
background-color: rgba(24, 23, 23, 1);
}
#nx-repo svg {
color: rgba(24, 23, 23, 1);
}
#nx-cloud {
margin-bottom: 2rem;
margin-top: 2rem;
padding: 2.5rem 2rem;
}
#nx-cloud > div {
align-items: center;
display: flex;
}
#nx-cloud > div svg {
border-radius: 0.375rem;
flex-shrink: 0;
width: 3rem;
}
#nx-cloud > div h2 {
font-size: 1.125rem;
font-weight: 400;
letter-spacing: -0.025em;
line-height: 1.75rem;
padding-left: 1rem;
padding-right: 1rem;
}
#nx-cloud > div h2 span {
display: block;
font-size: 0.875rem;
font-weight: 300;
line-height: 1.25rem;
}
#nx-cloud p {
font-size: 1rem;
line-height: 1.5rem;
margin-top: 1rem;
}
#nx-cloud pre {
margin-top: 1rem;
}
#nx-cloud a {
color: rgba(107, 114, 128, 1);
display: block;
font-size: 0.875rem;
line-height: 1.25rem;
margin-top: 1.5rem;
text-align: right;
}
#nx-cloud a:hover {
text-decoration: underline;
}
#commands {
padding: 2.5rem 2rem;
margin-top: 3.5rem;
}
#commands h2 {
font-size: 1.25rem;
font-weight: 400;
letter-spacing: -0.025em;
line-height: 1.75rem;
padding-left: 1rem;
padding-right: 1rem;
}
#commands p {
font-size: 1rem;
font-weight: 300;
line-height: 1.5rem;
margin-top: 1rem;
padding-left: 1rem;
padding-right: 1rem;
}
details {
align-items: center;
display: flex;
margin-top: 1rem;
padding-left: 1rem;
padding-right: 1rem;
width: 100%;
}
details pre > span {
color: rgba(181, 181, 181, 1);
}
summary {
border-radius: 0.5rem;
display: flex;
font-weight: 400;
padding: 0.5rem;
cursor: pointer;
transition-property:
background-color,
border-color,
color,
fill,
stroke,
opacity,
box-shadow,
transform,
filter,
backdrop-filter,
-webkit-backdrop-filter;
transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
transition-duration: 150ms;
}
summary:hover {
background-color: rgba(243, 244, 246, 1);
}
summary svg {
height: 1.5rem;
margin-right: 1rem;
width: 1.5rem;
}
#love {
color: rgba(107, 114, 128, 1);
font-size: 0.875rem;
line-height: 1.25rem;
margin-top: 3.5rem;
opacity: 0.6;
text-align: center;
}
#love svg {
color: rgba(252, 165, 165, 1);
width: 1.25rem;
height: 1.25rem;
display: inline;
margin-top: -0.25rem;
}
@media screen and (min-width: 768px) {
#hero {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
#hero .logo-container {
display: flex;
}
#middle-content {
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
</style>
<div class="wrapper">
<div class="container">
<!-- WELCOME -->
<div id="welcome">
<h1>
<span> Hello there, </span>
Welcome table-plugin 👋
</h1>
</div>
<!-- HERO -->
<div id="hero" class="rounded">
<div class="text-container">
<h2>
<svg
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 12l2 2 4-4M7.835 4.697a3.42 3.42 0 001.946-.806 3.42 3.42 0 014.438 0 3.42 3.42 0 001.946.806 3.42 3.42 0 013.138 3.138 3.42 3.42 0 00.806 1.946 3.42 3.42 0 010 4.438 3.42 3.42 0 00-.806 1.946 3.42 3.42 0 01-3.138 3.138 3.42 3.42 0 00-1.946.806 3.42 3.42 0 01-4.438 0 3.42 3.42 0 00-1.946-.806 3.42 3.42 0 01-3.138-3.138 3.42 3.42 0 00-.806-1.946 3.42 3.42 0 010-4.438 3.42 3.42 0 00.806-1.946 3.42 3.42 0 013.138-3.138z"
/>
</svg>
<span>You&apos;re up and running</span>
</h2>
<a href="#commands"> What&apos;s next? </a>
</div>
<div class="logo-container">
<svg
fill="currentColor"
role="img"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M11.987 14.138l-3.132 4.923-5.193-8.427-.012 8.822H0V4.544h3.691l5.247 8.833.005-3.998 3.044 4.759zm.601-5.761c.024-.048 0-3.784.008-3.833h-3.65c.002.059-.005 3.776-.003 3.833h3.645zm5.634 4.134a2.061 2.061 0 0 0-1.969 1.336 1.963 1.963 0 0 1 2.343-.739c.396.161.917.422 1.33.283a2.1 2.1 0 0 0-1.704-.88zm3.39 1.061c-.375-.13-.8-.277-1.109-.681-.06-.08-.116-.17-.176-.265a2.143 2.143 0 0 0-.533-.642c-.294-.216-.68-.322-1.18-.322a2.482 2.482 0 0 0-2.294 1.536 2.325 2.325 0 0 1 4.002.388.75.75 0 0 0 .836.334c.493-.105.46.36 1.203.518v-.133c-.003-.446-.246-.55-.75-.733zm2.024 1.266a.723.723 0 0 0 .347-.638c-.01-2.957-2.41-5.487-5.37-5.487a5.364 5.364 0 0 0-4.487 2.418c-.01-.026-1.522-2.39-1.538-2.418H8.943l3.463 5.423-3.379 5.32h3.54l1.54-2.366 1.568 2.366h3.541l-3.21-5.052a.7.7 0 0 1-.084-.32 2.69 2.69 0 0 1 2.69-2.691h.001c1.488 0 1.736.89 2.057 1.308.634.826 1.9.464 1.9 1.541a.707.707 0 0 0 1.066.596zm.35.133c-.173.372-.56.338-.755.639-.176.271.114.412.114.412s.337.156.538-.311c.104-.231.14-.488.103-.74z"
/>
</svg>
</div>
</div>
<!-- MIDDLE CONTENT -->
<div id="middle-content">
<div id="learning-materials" class="rounded shadow">
<h2>Learning materials</h2>
<a
href="https://nx.dev/getting-started/intro?utm_source=nx-project"
target="_blank"
rel="noreferrer"
class="list-item-link"
>
<svg
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"
/>
</svg>
<span>
Documentation
<span> Everything is in there </span>
</span>
<svg
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 5l7 7-7 7"
/>
</svg>
</a>
<a
href="https://blog.nrwl.io/?utm_source=nx-project"
target="_blank"
rel="noreferrer"
class="list-item-link"
>
<svg
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M19 20H5a2 2 0 01-2-2V6a2 2 0 012-2h10a2 2 0 012 2v1m2 13a2 2 0 01-2-2V7m2 13a2 2 0 002-2V9a2 2 0 00-2-2h-2m-4-3H9M7 16h6M7 8h6v4H7V8z"
/>
</svg>
<span>
Blog
<span> Changelog, features & events </span>
</span>
<svg
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 5l7 7-7 7"
/>
</svg>
</a>
<a
href="https://www.youtube.com/@NxDevtools/videos?utm_source=nx-project&sub_confirmation=1"
target="_blank"
rel="noreferrer"
class="list-item-link"
>
<svg
role="img"
viewBox="0 0 24 24"
fill="currentColor"
xmlns="http://www.w3.org/2000/svg"
>
<title>YouTube</title>
<path
d="M23.498 6.186a3.016 3.016 0 0 0-2.122-2.136C19.505 3.545 12 3.545 12 3.545s-7.505 0-9.377.505A3.017 3.017 0 0 0 .502 6.186C0 8.07 0 12 0 12s0 3.93.502 5.814a3.016 3.016 0 0 0 2.122 2.136c1.871.505 9.376.505 9.376.505s7.505 0 9.377-.505a3.015 3.015 0 0 0 2.122-2.136C24 15.93 24 12 24 12s0-3.93-.502-5.814zM9.545 15.568V8.432L15.818 12l-6.273 3.568z"
/>
</svg>
<span>
YouTube channel
<span> Nx Show, talks & tutorials </span>
</span>
<svg
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 5l7 7-7 7"
/>
</svg>
</a>
<a
href="https://nx.dev/angular-tutorial/1-code-generation?utm_source=nx-project"
target="_blank"
rel="noreferrer"
class="list-item-link"
>
<svg
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M15 15l-2 5L9 9l11 4-5 2zm0 0l5 5M7.188 2.239l.777 2.897M5.136 7.965l-2.898-.777M13.95 4.05l-2.122 2.122m-5.657 5.656l-2.12 2.122"
/>
</svg>
<span>
Interactive tutorials
<span> Create an app, step-by-step </span>
</span>
<svg
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 5l7 7-7 7"
/>
</svg>
</a>
<a
href="https://nxplaybook.com/?utm_source=nx-project"
target="_blank"
rel="noreferrer"
class="list-item-link"
>
<svg
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path d="M12 14l9-5-9-5-9 5 9 5z" />
<path
d="M12 14l6.16-3.422a12.083 12.083 0 01.665 6.479A11.952 11.952 0 0012 20.055a11.952 11.952 0 00-6.824-2.998 12.078 12.078 0 01.665-6.479L12 14z"
/>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M12 14l9-5-9-5-9 5 9 5zm0 0l6.16-3.422a12.083 12.083 0 01.665 6.479A11.952 11.952 0 0012 20.055a11.952 11.952 0 00-6.824-2.998 12.078 12.078 0 01.665-6.479L12 14zm-4 6v-7.5l4-2.222"
/>
</svg>
<span>
Video courses
<span> Nx custom courses </span>
</span>
<svg
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 5l7 7-7 7"
/>
</svg>
</a>
</div>
<div id="other-links">
<a
class="button-pill rounded shadow nx-console"
href="https://marketplace.visualstudio.com/items?itemName=nrwl.angular-console&utm_source=nx-project"
target="_blank"
rel="noreferrer"
>
<svg
fill="currentColor"
role="img"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<title>Visual Studio Code</title>
<path
d="M23.15 2.587L18.21.21a1.494 1.494 0 0 0-1.705.29l-9.46 8.63-4.12-3.128a.999.999 0 0 0-1.276.057L.327 7.261A1 1 0 0 0 .326 8.74L3.899 12 .326 15.26a1 1 0 0 0 .001 1.479L1.65 17.94a.999.999 0 0 0 1.276.057l4.12-3.128 9.46 8.63a1.492 1.492 0 0 0 1.704.29l4.942-2.377A1.5 1.5 0 0 0 24 20.06V3.939a1.5 1.5 0 0 0-.85-1.352zm-5.146 14.861L10.826 12l7.178-5.448v10.896z"
/>
</svg>
<span>
Install Nx Console for VSCode
<span>The official VSCode extension for Nx.</span>
</span>
</a>
<a
class="button-pill rounded shadow nx-console-jetbrains"
href="https://plugins.jetbrains.com/plugin/21060-nx-console"
target="_blank"
rel="noreferrer"
>
<svg
height="48"
width="48"
viewBox="20 20 60 60"
xmlns="http://www.w3.org/2000/svg"
>
<path d="m22.5 22.5h60v60h-60z" />
<g fill="#fff">
<path d="m29.03 71.25h22.5v3.75h-22.5z" />
<path
d="m28.09 38 1.67-1.58a1.88 1.88 0 0 0 1.47.87c.64 0 1.06-.44 1.06-1.31v-5.98h2.58v6a3.48 3.48 0 0 1 -.87 2.6 3.56 3.56 0 0 1 -2.57.95 3.84 3.84 0 0 1 -3.34-1.55z"
/>
<path
d="m36 30h7.53v2.19h-5v1.44h4.49v2h-4.42v1.49h5v2.21h-7.6z"
/>
<path d="m47.23 32.29h-2.8v-2.29h8.21v2.27h-2.81v7.1h-2.6z" />
<path
d="m29.13 43.08h4.42a3.53 3.53 0 0 1 2.55.83 2.09 2.09 0 0 1 .6 1.53 2.16 2.16 0 0 1 -1.44 2.09 2.27 2.27 0 0 1 1.86 2.29c0 1.61-1.31 2.59-3.55 2.59h-4.44zm5 2.89c0-.52-.42-.8-1.18-.8h-1.29v1.64h1.24c.79 0 1.25-.26 1.25-.81zm-.9 2.66h-1.57v1.73h1.62c.8 0 1.24-.31 1.24-.86 0-.5-.4-.87-1.27-.87z"
/>
<path
d="m38 43.08h4.1a4.19 4.19 0 0 1 3 1 2.93 2.93 0 0 1 .9 2.19 3 3 0 0 1 -1.93 2.89l2.24 3.27h-3l-1.88-2.84h-.87v2.84h-2.56zm4 4.5c.87 0 1.39-.43 1.39-1.11 0-.75-.54-1.12-1.4-1.12h-1.44v2.26z"
/>
<path
d="m49.59 43h2.5l4 9.44h-2.79l-.67-1.69h-3.63l-.67 1.69h-2.71zm2.27 5.73-1-2.65-1.06 2.65z"
/>
<path d="m56.46 43.05h2.6v9.37h-2.6z" />
<path
d="m60.06 43.05h2.42l3.37 5v-5h2.57v9.37h-2.26l-3.53-5.14v5.14h-2.57z"
/>
<path
d="m68.86 51 1.45-1.73a4.84 4.84 0 0 0 3 1.13c.71 0 1.08-.24 1.08-.65 0-.4-.31-.6-1.59-.91-2-.46-3.53-1-3.53-2.93 0-1.74 1.37-3 3.62-3a5.89 5.89 0 0 1 3.86 1.25l-1.26 1.84a4.63 4.63 0 0 0 -2.62-.92c-.63 0-.94.25-.94.6 0 .42.32.61 1.63.91 2.14.46 3.44 1.16 3.44 2.91 0 1.91-1.51 3-3.79 3a6.58 6.58 0 0 1 -4.35-1.5z"
/>
</g>
</svg>
<span>
Install Nx Console for JetBrains
<span
>Available for WebStorm, Intellij IDEA Ultimate and
more!</span
>
</span>
</a>
<div id="nx-cloud" class="rounded shadow">
<div>
<svg
id="nx-cloud-logo"
role="img"
xmlns="http://www.w3.org/2000/svg"
stroke="currentColor"
fill="transparent"
viewBox="0 0 24 24"
>
<path
stroke-width="2"
d="M23 3.75V6.5c-3.036 0-5.5 2.464-5.5 5.5s-2.464 5.5-5.5 5.5-5.5 2.464-5.5 5.5H3.75C2.232 23 1 21.768 1 20.25V3.75C1 2.232 2.232 1 3.75 1h16.5C21.768 1 23 2.232 23 3.75Z"
/>
<path
stroke-width="2"
d="M23 6v14.1667C23 21.7307 21.7307 23 20.1667 23H6c0-3.128 2.53867-5.6667 5.6667-5.6667 3.128 0 5.6666-2.5386 5.6666-5.6666C17.3333 8.53867 19.872 6 23 6Z"
/>
</svg>
<h2>
Nx Cloud
<span> Enable faster CI & better DX </span>
</h2>
</div>
<p>
You can activate distributed tasks executions and caching by
running:
</p>
<pre>nx connect-to-nx-cloud</pre>
<a
href="https://nx.app/?utm_source=nx-project"
target="_blank"
rel="noreferrer"
>
What is Nx Cloud?
</a>
</div>
<a
id="nx-repo"
class="button-pill rounded shadow"
href="https://github.com/nrwl/nx?utm_source=nx-project"
target="_blank"
rel="noreferrer"
>
<svg
fill="currentColor"
role="img"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
d="M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12"
/>
</svg>
<span>
Nx is open source
<span> Love Nx? Give us a star! </span>
</span>
</a>
</div>
</div>
<!-- COMMANDS -->
<div id="commands" class="rounded shadow">
<h2>Next steps</h2>
<p>Here are some things you can do with Nx:</p>
<details>
<summary>
<svg
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
/>
</svg>
Add UI library
</summary>
<pre><span># Generate UI lib</span>
nx g &#64;nx/angular:lib ui
<span># Add a component</span>
nx g &#64;nx/angular:component ui/src/lib/button</pre>
</details>
<details>
<summary>
<svg
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
/>
</svg>
View project details
</summary>
<pre>nx show project table-plugin --web</pre>
</details>
<details>
<summary>
<svg
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
/>
</svg>
View interactive project graph
</summary>
<pre>nx graph</pre>
</details>
<details>
<summary>
<svg
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M8 9l3 3-3 3m5 0h3M5 20h14a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"
/>
</svg>
Run affected commands
</summary>
<pre><span># see what&apos;s been affected by changes</span>
nx affected:graph
<span># run tests for current changes</span>
nx affected:test
<span># run e2e tests for current changes</span>
nx affected:e2e</pre>
</details>
</div>
<p id="love">
Carefully crafted with
<svg
fill="currentColor"
stroke="none"
viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg"
>
<path
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M4.318 6.318a4.5 4.5 0 000 6.364L12 20.364l7.682-7.682a4.5 4.5 0 00-6.364-6.364L12 7.636l-1.318-1.318a4.5 4.5 0 00-6.364 0z"
/>
</svg>
</p>
</div>
</div>
`,
styles: [],
encapsulation: ViewEncapsulation.None,
})
export class NxWelcomeComponent {}

View File

@ -10,12 +10,17 @@ Let's dive in.
### Step 1: Initialize the Plugin ### Step 1: Initialize the Plugin
First, you need to create the scaffolding for your plugin. Use the following command, replacing `example-plugin` with the name of your plugin: First, you need to create the scaffolding for your plugin. Use the Angular CLI to generate a new application:
```sh ```sh
pnpx nx g @nx/angular:app example-plugin --directory=apps/example-plugin --bundler=esbuild cd apps
ng new example-plugin --style=css --routing=false --skip-git --skip-install
cd ..
pnpm install
``` ```
Then register it in `angular.json` by adding a new project entry (see existing plugins for reference).
### Step 2: Configure the Manifest ### Step 2: Configure the Manifest
Next, create a `manifest.json` file inside the `/src/assets` directory. This file is crucial as it defines key properties of your plugin, including permissions and the entry point script. Next, create a `manifest.json` file inside the `/src/assets` directory. This file is crucial as it defines key properties of your plugin, including permissions and the entry point script.
@ -38,40 +43,41 @@ Next, create a `manifest.json` file inside the `/src/assets` directory. This fil
### Step 3: Update Project Configuration ### Step 3: Update Project Configuration
Now, add the plugin tag. Update `angular.json` to configure the build for your plugin. Add to the project's `architect.build.options`:
```typescript
"tags": ["type:plugin"],
```
Also, update `targets.build` with the following code to allow the use of Penpot styles and build the plugin code.
```json ```json
"options": { "styles": [
"styles": [ "libs/plugins-styles/src/lib/styles.css",
"libs/plugins-styles/src/lib/styles.css", "apps/example-plugin/src/styles.css"
"apps/example-plugin/src/styles.css" ],
], "optimization": {
"optimization": { "scripts": true,
"scripts": true, "styles": true,
"styles": true, "fonts": false
"fonts": false
}
},
"dependsOn": ["buildPlugin"]
```
Add the default port to the `serve.configurations.development` task:
```json
"development": {
// ...
"port": 4302,
} }
``` ```
Add a custom port to `architect.serve.options`:
```json
"port": 4302
```
For choosing the port go check the Sample Plugins table at the [README](../README.md) so your plugin doesn't use a duplicate port. We're using the range 4300-4399. For choosing the port go check the Sample Plugins table at the [README](../README.md) so your plugin doesn't use a duplicate port. We're using the range 4300-4399.
Create a `package.json` in your plugin folder with build scripts:
```json
{
"name": "example-plugin",
"scripts": {
"build": "ng build example-plugin",
"serve": "ng serve example-plugin",
"lint": "eslint src --ext .ts,.html"
}
}
```
### Step 4: Modify TypeScript Configuration ### Step 4: Modify TypeScript Configuration
Create ``tsconfig.plugin.json` next to the `tsconfig.json`: Create ``tsconfig.plugin.json` next to the `tsconfig.json`:
@ -125,10 +131,10 @@ console.log('Hello Plugin');
Run this command: Run this command:
```sh ```sh
pnpx nx run example-plugin:init pnpm --filter example-plugin serve
``` ```
This will run two tasks: `serve`, the usual Angular server, and `buildPlugin`, which will compile the `plugin.ts` file. This will start the Angular development server.
### Step 8: Load the Plugin in Penpot ### Step 8: Load the Plugin in Penpot
@ -142,8 +148,8 @@ You can also open the Plugin manager modal via:
### Step 9: Build plugin ### Step 9: Build plugin
``` ```sh
pnpx nx run example-plugin:build pnpm --filter example-plugin build
``` ```
### Learn More About Plugin Development ### Learn More About Plugin Development

View File

@ -10,15 +10,50 @@ Let's dive in.
### Step 1: Initialize the Plugin ### Step 1: Initialize the Plugin
First, you need to create the scaffolding for your plugin. Use the following command, replacing `example-plugin` with the name of your plugin: First, you need to create the scaffolding for your plugin. Create a new folder for your plugin:
```sh ```sh
pnpx nx g @nx/web:application example-plugin --directory=apps/example-plugin mkdir -p apps/example-plugin/src apps/example-plugin/public
``` ```
### Step 2: Migrate eslint to ESM Create a basic `package.json` in `apps/example-plugin`:
Replace `module.exports = [` with `export default [` and const `baseConfig = require('../../eslint.base.config.js');` with `import baseConfig from '../../eslint.config.js';`. ```json
{
"name": "example-plugin",
"private": true,
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"lint": "eslint src --ext .ts"
}
}
```
Create a `vite.config.ts` file (see `apps/create-palette-plugin/vite.config.ts` for reference).
### Step 2: Create ESLint Config
Create an `eslint.config.js` file:
```js
import tseslint from 'typescript-eslint';
import eslintConfigPrettier from 'eslint-config-prettier';
export default tseslint.config(
...tseslint.configs.recommended,
eslintConfigPrettier,
{
languageOptions: {
parserOptions: {
project: './tsconfig.*?.json',
tsconfigRootDir: import.meta.dirname,
},
},
},
);
```
### Step 3: Configure the Manifest ### Step 3: Configure the Manifest
@ -73,25 +108,16 @@ Update your `tsconfig.app.json` to include the necessary TypeScript files for yo
To preview your plugin, start a static server by running: To preview your plugin, start a static server by running:
```sh ```sh
pnpx nx run example-plugin:build --watch & pnpx nx run example-plugin:preview pnpm --filter example-plugin dev
``` ```
### Step 7: Add TS parser to eslint Or for production preview:
Add these options to the end of the `eslint.config.js` file to allow linting with type information: ```sh
pnpm --filter example-plugin build && pnpm --filter example-plugin preview
```js
{
languageOptions: {
parserOptions: {
project: './tsconfig.*?.json',
tsconfigRootDir: import.meta.dirname,
},
},
},
``` ```
### Step 8: Load the Plugin in Penpot ### Step 7: Load the Plugin in Penpot
To load your plugin into Penpot you can use the shortcut `Ctrl + Alt + P` to directly open the Plugin manager modal. There you need to provide the plugin's manifest URL (example: `http://plugin.example/manifest.json`) for the installation. If there's no issues the plugin will be installed and then you would be able to open it whenever you like. To load your plugin into Penpot you can use the shortcut `Ctrl + Alt + P` to directly open the Plugin manager modal. There you need to provide the plugin's manifest URL (example: `http://plugin.example/manifest.json`) for the installation. If there's no issues the plugin will be installed and then you would be able to open it whenever you like.

View File

@ -11,15 +11,6 @@ packages and managing releases.
Before generating the release, please, check the update the changelog with Before generating the release, please, check the update the changelog with
the changes that will be released. the changes that will be released.
## Problem with pnpm
There is an issue with dependencies and release with pnpm. For it to work
you need to add the following into your `.npmrc`
```
link-workspace-packages=true
```
## NPM Authentication ## NPM Authentication
You need to generate a temporary access token in the NPM website. You need to generate a temporary access token in the NPM website.
@ -45,21 +36,28 @@ expected, run the following command:
```shell ```shell
git checkout main git checkout main
pnpm run release pnpm run release -- --version patch
``` ```
The `--version` option accepts:
- `patch` - Increments the patch version (1.0.0 → 1.0.1)
- `minor` - Increments the minor version (1.0.0 → 1.1.0)
- `major` - Increments the major version (1.0.0 → 2.0.0)
- An exact version like `1.5.0`
### Generating a Real Release ### Generating a Real Release
To create an actual release, disable the dry-run option: To create an actual release, disable the dry-run option:
```shell ```shell
pnpm run release -- --dry-run false pnpm run release -- --version patch --dry-run false
``` ```
This command will: This command will:
- Update the library's `package.json` version - Update the library's `package.json` version
- Generate a commit - Build all packages
- Publish to NPM with the `latest` tag - Publish to NPM with the `latest` tag
Ensure everything is correct before proceeding with the git push. Once Ensure everything is correct before proceeding with the git push. Once
@ -70,20 +68,17 @@ git commit -m ":arrow_up: Updated plugins release to X.X.X"
git push git push
``` ```
For detailed information, refer to the [Nx Release
Documentation](https://nx.dev/recipes/nx-release/get-started-with-nx-release).
### Creating a Preview Version ### Creating a Preview Version
To generate a preview version and avoid publishing it as the latest release, use: To generate a preview version and avoid publishing it as the latest release, use:
```shell ```shell
pnpm run release -- --dry-run false --latest false --preid next pnpm run release -- --version prepatch --dry-run false --latest false --preid next
``` ```
For example, if the current version is `0.8.0` and you select the For example, if the current version is `0.8.0` and you use `prepatch`,
`prepatch` option as a version specifier, it will generate the version it will generate the version `0.8.1-next.0` and publish it with the
`0.8.1-next.0` and publish it with the next tag on npm. `next` tag on npm.
### Help ### Help
@ -104,4 +99,3 @@ pnpm run release -- --help
- **CSS Build Script**: `./tools/scripts/build-css.mjs` - **CSS Build Script**: `./tools/scripts/build-css.mjs`
- **Types Build Script**: `./tools/scripts/build-types.mjs` - **Types Build Script**: `./tools/scripts/build-types.mjs`
- **Release Script**: `./tools/scripts/publish.ts` - **Release Script**: `./tools/scripts/publish.ts`
- **Publish config**: `./nx.json`

View File

@ -1,49 +0,0 @@
import { FlatCompat } from '@eslint/eslintrc';
import nxEslintPlugin from '@nx/eslint-plugin';
import js from '@eslint/js';
import path from 'path';
import { fileURLToPath } from 'url';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
export const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
});
export default [
js.configs.recommended,
{ plugins: { '@nx': nxEslintPlugin } },
{
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
rules: {
'@nx/enforce-module-boundaries': [
'error',
{
enforceBuildableLibDependency: true,
allow: [],
depConstraints: [
{
sourceTag: '*',
onlyDependOnLibsWithTags: ['*'],
},
],
},
],
},
},
...compat.config({ extends: ['plugin:@nx/typescript'] }).map((config) => ({
...config,
files: ['**/*.ts', '**/*.tsx'],
rules: {},
})),
...compat.config({ extends: ['plugin:@nx/javascript'] }).map((config) => ({
...config,
files: ['**/*.js', '**/*.jsx'],
rules: {},
})),
{
ignores: ['eslint.config.js', 'vite.config.{js,ts,mjs,mts}'],
},
];

View File

@ -1,122 +1,66 @@
import baseConfig, { compat } from './eslint.base.config.js'; import js from '@eslint/js';
import tseslint from 'typescript-eslint';
import globals from 'globals'; import globals from 'globals';
import eslintConfigPrettier from 'eslint-config-prettier';
export default [ export default [
...baseConfig, js.configs.recommended,
...tseslint.configs.recommended.map((config) => ({
...config,
files: config.files || ['**/*.ts', '**/*.tsx', '**/*.mts', '**/*.cts'],
})),
eslintConfigPrettier,
{ {
files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'], files: ['**/*.ts', '**/*.tsx', '**/*.js', '**/*.jsx'],
rules: {
'@nx/enforce-module-boundaries': [
'error',
{
enforceBuildableLibDependency: true,
allow: [],
depConstraints: [
{
sourceTag: 'type:plugin',
onlyDependOnLibsWithTags: [
'type:util',
'type:ui',
'type:feature',
],
},
{
sourceTag: 'type:app',
onlyDependOnLibsWithTags: [
'type:util',
'type:ui',
'type:feature',
],
},
{
sourceTag: 'type:feature',
onlyDependOnLibsWithTags: [
'type:feature',
'type:ui',
'type:util',
],
},
{
sourceTag: 'type:ui',
onlyDependOnLibsWithTags: ['type:ui', 'type:util'],
},
{
sourceTag: 'type:util',
onlyDependOnLibsWithTags: ['type:util'],
},
{
sourceTag: 'type:e2e',
onlyDependOnLibsWithTags: ['type:ui', 'type:util'],
},
],
},
],
},
languageOptions: { languageOptions: {
globals: { globals: {
...globals.browser,
...globals.node,
penpot: 'readonly', penpot: 'readonly',
repairIntrinsics: 'readonly', repairIntrinsics: 'readonly',
hardenIntrinsics: 'readonly', hardenIntrinsics: 'readonly',
Compartment: 'readonly', Compartment: 'readonly',
harden: 'readonly', harden: 'readonly',
}, },
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
},
},
rules: {
'@typescript-eslint/no-unused-vars': [
'error',
{ argsIgnorePattern: '^_' },
],
'no-multiple-empty-lines': ['error', { max: 1 }],
quotes: ['error', 'single', { avoidEscape: true }],
}, },
}, },
...compat {
.config({ files: ['**/*.spec.ts', '**/*.test.ts'],
extends: [ languageOptions: {
'plugin:@typescript-eslint/recommended', globals: {
'plugin:@typescript-eslint/recommended-requiring-type-checking', ...globals.jest,
'plugin:deprecation/recommended',
'prettier',
],
})
.map((config) => ({
...config,
files: ['**/*.ts', '**/*.tsx'],
rules: {
'@typescript-eslint/no-unused-vars': ['error'],
'no-multiple-empty-lines': [2, { max: 1 }],
quotes: ['error', 'single', { avoidEscape: true }],
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': ['error'],
}, },
})), },
...compat rules: {
.config({ '@typescript-eslint/no-explicit-any': 'off',
extends: [ },
'plugin:@typescript-eslint/recommended', },
'plugin:@typescript-eslint/recommended-requiring-type-checking',
'plugin:deprecation/recommended',
'prettier',
],
})
.map((config) => ({
...config,
files: ['**/*.spec.ts'],
rules: {
'@typescript-eslint/no-unused-vars': ['error'],
'no-multiple-empty-lines': [2, { max: 1 }],
quotes: ['error', 'single', { avoidEscape: true }],
'@typescript-eslint/no-unsafe-member-access': 'off',
'@typescript-eslint/no-unsafe-call': 'off',
'@typescript-eslint/no-unsafe-assignment': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-unsafe-argument': 'off',
'@typescript-eslint/no-unsafe-return': 'off',
'@ngrx/prefix-selectors-with-select': 'off',
},
languageOptions: {
globals: {
...globals.jest,
},
},
})),
{ {
files: ['**/*.js', '**/*.jsx'], files: ['**/*.js', '**/*.jsx'],
rules: {}, rules: {},
}, },
{ {
ignores: ['**/vite.config.*.timestamp*', '**/vitest.config.*.timestamp*'], ignores: [
'node_modules',
'dist',
'**/dist/**',
'**/.vite/**',
'eslint.config.js',
'vite.config.{js,ts,mjs,mts}',
'**/vite.config.*.timestamp*',
'**/vitest.config.*.timestamp*',
],
}, },
]; ];

View File

@ -2,5 +2,9 @@
"name": "@penpot/plugin-types", "name": "@penpot/plugin-types",
"version": "1.4.2", "version": "1.4.2",
"typings": "./index.d.ts", "typings": "./index.d.ts",
"type": "module" "type": "module",
"scripts": {
"build": "node ../../tools/scripts/build-types.mjs",
"lint": "tsc -p . --noEmit"
}
} }

View File

@ -3,16 +3,5 @@
"$schema": "../../node_modules/nx/schemas/project-schema.json", "$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "libs/plugin-types", "sourceRoot": "libs/plugin-types",
"projectType": "library", "projectType": "library",
"targets": { "targets": {}
"lint": {
"command": "tsc -p {projectRoot} --noEmit"
},
"build": {
"command": "node tools/scripts/build-types.mjs",
"options": {
"outputPath": "dist/plugin-types"
}
}
},
"tags": ["type:ui"]
} }

View File

@ -31,17 +31,6 @@ export default [
languageOptions: { languageOptions: {
parser: jsoncParser, parser: jsoncParser,
}, },
rules: { rules: {},
'@nx/dependency-checks': [
'error',
{
ignoredFiles: [
'libs/plugins-runtime/vite.config.ts',
'libs/plugins-runtime/eslint.config.js',
'libs/plugins-runtime/**/*.spec.ts',
],
},
],
},
}, },
]; ];

View File

@ -8,5 +8,12 @@
}, },
"module": "./index.mjs", "module": "./index.mjs",
"typings": "./index.d.ts", "typings": "./index.d.ts",
"type": "module" "type": "module",
"scripts": {
"build": "vite build",
"build:watch": "vite build --watch --mode development",
"preview": "vite preview",
"lint": "eslint .",
"test": "vitest"
}
} }

View File

@ -1,14 +1,7 @@
{ {
"name": "plugins-runtime", "name": "plugins-runtime",
"$schema": "../node_modules/nx/schemas/project-schema.json", "$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "plugins-runtime/src", "sourceRoot": "libs/plugins-runtime/src",
"projectType": "library", "projectType": "library",
"targets": { "targets": {}
"preview": {
"command": "vite preview",
"options": { "cwd": "libs/plugins-runtime" },
"_": "Delete this targets:preview when https://github.com/nrwl/nx/issues/26800 is fixed"
}
},
"tags": ["type:feature"]
} }

View File

@ -21,6 +21,7 @@ repairIntrinsics({
unhandledRejectionTrapping: 'none', unhandledRejectionTrapping: 'none',
}); });
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const globalThisAny$ = globalThis as any; const globalThisAny$ = globalThis as any;
export const initPluginsRuntime = (contextBuilder: (id: string) => Context) => { export const initPluginsRuntime = (contextBuilder: (id: string) => Context) => {

View File

@ -84,6 +84,7 @@ export function createApi(
utils: { utils: {
geometry: { geometry: {
center(shapes: Shape[]) { center(shapes: Shape[]) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
return (window as any).app.plugins.public_utils.centerShapes(shapes); return (window as any).app.plugins.public_utils.centerShapes(shapes);
}, },
}, },

View File

@ -53,11 +53,11 @@ export function resizeModal(
const minPluginWidth = 200; const minPluginWidth = 200;
const minPluginHeight = 200; const minPluginHeight = 200;
let wrapper = modal.shadowRoot?.querySelector('.wrapper'); const wrapper = modal.shadowRoot?.querySelector('.wrapper');
let curX = 0; let curX = 0;
let curY = 0; let curY = 0;
if (wrapper) { if (wrapper) {
let rect = wrapper.getBoundingClientRect(); const rect = wrapper.getBoundingClientRect();
curX = rect.x; curX = rect.x;
curY = rect.y; curY = rect.y;
} }

View File

@ -117,7 +117,7 @@ export function createSandbox(
// Window properties // Window properties
console: ses.harden(window.console), console: ses.harden(window.console),
devicePixelRatio: ses.harden(window.devicePixelRatio), devicePixelRatio: window.devicePixelRatio,
atob: ses.harden(window.atob.bind(null)), atob: ses.harden(window.atob.bind(null)),
btoa: ses.harden(window.btoa.bind(null)), btoa: ses.harden(window.btoa.bind(null)),
structuredClone: ses.harden(window.structuredClone), structuredClone: ses.harden(window.structuredClone),

View File

@ -53,7 +53,10 @@ export const loadPlugin = async function (
manifest, manifest,
() => { () => {
plugins = plugins.filter((api) => api !== plugin); plugins = plugins.filter((api) => api !== plugin);
closeCallback && closeCallback();
if (closeCallback) {
closeCallback();
}
}, },
); );

View File

@ -7,10 +7,10 @@ export const ses = {
hardenIntrinsics(); hardenIntrinsics();
} }
}, },
createCompartment: (globals?: Object) => { createCompartment: (globals?: object) => {
return new Compartment(globals); return new Compartment(globals);
}, },
harden: (obj: Object) => { harden: (obj: object) => {
return harden(obj); return harden(obj);
}, },
safeReturn<T>(value: T): T { safeReturn<T>(value: T): T {

View File

@ -4,7 +4,7 @@ import { defineConfig } from 'vite';
import dts from 'vite-plugin-dts'; import dts from 'vite-plugin-dts';
import { viteStaticCopy } from 'vite-plugin-static-copy'; import { viteStaticCopy } from 'vite-plugin-static-copy';
import * as path from 'path'; import * as path from 'path';
import { nxViteTsPaths } from '@nx/vite/plugins/nx-tsconfig-paths.plugin'; import tsconfigPaths from 'vite-tsconfig-paths';
import checker from 'vite-plugin-checker'; import checker from 'vite-plugin-checker';
export default defineConfig({ export default defineConfig({
@ -12,7 +12,7 @@ export default defineConfig({
cacheDir: '../node_modules/.vite/plugins-runtime', cacheDir: '../node_modules/.vite/plugins-runtime',
plugins: [ plugins: [
nxViteTsPaths(), tsconfigPaths(),
dts({ dts({
entryRoot: 'src', entryRoot: 'src',
tsconfigPath: path.join(__dirname, 'tsconfig.lib.json'), tsconfigPath: path.join(__dirname, 'tsconfig.lib.json'),
@ -32,11 +32,6 @@ export default defineConfig({
}), }),
], ],
// Uncomment this if you are using workers.
// worker: {
// plugins: [ nxViteTsPaths() ],
// },
// Configuration for building your library. // Configuration for building your library.
// See: https://vitejs.dev/guide/build.html#library-mode // See: https://vitejs.dev/guide/build.html#library-mode
build: { build: {
@ -67,9 +62,6 @@ export default defineConfig({
test: { test: {
watch: false, watch: false,
globals: true, globals: true,
cache: {
dir: '../node_modules/.vitest',
},
environment: 'happy-dom', environment: 'happy-dom',
include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'],

View File

@ -1,5 +1,9 @@
{ {
"name": "@penpot/plugin-styles", "name": "@penpot/plugin-styles",
"version": "1.4.2", "version": "1.4.2",
"dependencies": {} "dependencies": {},
"scripts": {
"build": "node ../../tools/scripts/build-css.mjs",
"lint": "echo 0"
}
} }

View File

@ -1,18 +1,7 @@
{ {
"name": "plugins-styles", "name": "plugins-styles",
"$schema": "../node_modules/nx/schemas/project-schema.json", "$schema": "../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "plugins-styles/src", "sourceRoot": "libs/plugins-styles/src",
"projectType": "library", "projectType": "library",
"targets": { "targets": {}
"build": {
"command": "node tools/scripts/build-css.mjs",
"options": {
"outputPath": "dist/plugins-styles"
}
},
"lint": {
"command": "echo 0"
}
},
"tags": ["type:ui"]
} }

View File

@ -1,145 +0,0 @@
{
"pluginsConfig": {
"@nx/js": {
"analyzeSourceFiles": true
}
},
"cli": {
"packageManager": "pnpm"
},
"release": {
"projects": [
"libs/plugins-styles",
"libs/plugin-types",
"libs/plugins-runtime"
]
},
"extends": "nx/presets/npm.json",
"$schema": "./node_modules/nx/schemas/nx-schema.json",
"targetDefaults": {
"nx-release-publish": {
"options": {
"packageRoot": "dist/{projectName}"
}
},
"@nx/vite:build": {
"cache": true,
"dependsOn": ["^build"],
"inputs": ["default", "^default"]
},
"@nx/esbuild:esbuild": {
"cache": true,
"dependsOn": ["^build"],
"inputs": ["default", "^default"]
},
"@nx/js:tsc": {
"cache": true,
"dependsOn": ["^build"],
"inputs": ["default", "^default"]
},
"@angular-devkit/build-angular:application": {
"cache": true,
"dependsOn": ["^build"],
"inputs": ["default", "^default"]
}
},
"plugins": [
{
"plugin": "./tools/plugins/plugin-tasks.ts"
},
{
"plugin": "@nx/vite/plugin",
"options": {
"buildTargetName": "build",
"previewTargetName": "preview",
"serveTargetName": "serve",
"serveStaticTargetName": "serve-static"
}
},
{
"plugin": "@nx/eslint/plugin",
"options": {
"targetName": "lint"
}
},
{
"plugin": "@nx/vitest",
"options": {
"testTargetName": "test"
}
}
],
"generators": {
"@nx/web:application": {
"style": "css",
"linter": "eslint",
"unitTestRunner": "vitest",
"e2eTestRunner": "none",
"projectNameAndRootFormat": "as-provided",
"bundler": "vite"
},
"@nx/angular:application": {
"e2eTestRunner": "none",
"linter": "eslint",
"style": "css",
"unitTestRunner": "none",
"projectNameAndRootFormat": "as-provided"
},
"@nx/angular:component": {
"type": "component"
},
"@schematics/angular:component": {
"type": "component"
},
"@nx/angular:directive": {
"type": "directive"
},
"@schematics/angular:directive": {
"type": "directive"
},
"@nx/angular:service": {
"type": "service"
},
"@schematics/angular:service": {
"type": "service"
},
"@nx/angular:scam": {
"type": "component"
},
"@nx/angular:scam-directive": {
"type": "directive"
},
"@nx/angular:guard": {
"typeSeparator": "."
},
"@schematics/angular:guard": {
"typeSeparator": "."
},
"@nx/angular:interceptor": {
"typeSeparator": "."
},
"@schematics/angular:interceptor": {
"typeSeparator": "."
},
"@nx/angular:module": {
"typeSeparator": "."
},
"@schematics/angular:module": {
"typeSeparator": "."
},
"@nx/angular:pipe": {
"typeSeparator": "."
},
"@schematics/angular:pipe": {
"typeSeparator": "."
},
"@nx/angular:resolver": {
"typeSeparator": "."
},
"@schematics/angular:resolver": {
"typeSeparator": "."
}
},
"useLegacyCache": false,
"nxCloudId": "69720151957fca05c743ec9a"
}

View File

@ -6,27 +6,25 @@
"packageManager": "pnpm@10.26.2+sha512.0e308ff2005fc7410366f154f625f6631ab2b16b1d2e70238444dd6ae9d630a8482d92a451144debc492416896ed16f7b114a86ec68b8404b2443869e68ffda6", "packageManager": "pnpm@10.26.2+sha512.0e308ff2005fc7410366f154f625f6631ab2b16b1d2e70238444dd6ae9d630a8482d92a451144debc492416896ed16f7b114a86ec68b8404b2443869e68ffda6",
"scripts": { "scripts": {
"start": "pnpm run start:app:runtime", "start": "pnpm run start:app:runtime",
"start:app:runtime": "concurrently --kill-others --names build,server \"nx run plugins-runtime:build --watch --mode development\" \"nx run plugins-runtime:preview\"", "start:app:runtime": "concurrently --kill-others --names build,server \"pnpm --filter @penpot/plugins-runtime build:watch\" \"pnpm --filter @penpot/plugins-runtime preview\"",
"start:app:styles-example": "nx run example-styles:serve --host 0.0.0.0 --port 4201", "start:app:styles-example": "pnpm --filter example-styles dev",
"start:plugin:all": "concurrently --kill-others \"pnpm:start:plugin:*(!all)\"", "start:plugin:all": "concurrently --kill-others \"pnpm:start:plugin:*(!all)\"",
"start:plugin:poc-state": "nx run poc-state-plugin:init", "start:plugin:poc-state": "pnpm --filter poc-state-plugin serve",
"start:plugin:contrast": "nx run contrast-plugin:init", "start:plugin:contrast": "pnpm --filter contrast-plugin serve",
"start:plugin:icons": "nx run icons-plugin:init", "start:plugin:icons": "pnpm --filter icons-plugin serve",
"start:plugin:loremipsum": "nx run lorem-ipsum-plugin:init", "start:plugin:loremipsum": "pnpm --filter lorem-ipsum-plugin serve",
"start:plugin:palette": "nx run create-palette-plugin:build --watch & nx run create-palette-plugin:preview", "start:plugin:palette": "pnpm --filter create-palette-plugin build:watch & pnpm --filter create-palette-plugin preview",
"start:plugin:table": "nx run table-plugin:init", "start:plugin:table": "pnpm --filter table-plugin serve",
"start:plugin:renamelayers": "nx run rename-layers-plugin:init", "start:plugin:renamelayers": "pnpm --filter rename-layers-plugin serve",
"start:plugin:colors-to-tokens": "nx run colors-to-tokens-plugin:init", "start:plugin:colors-to-tokens": "pnpm --filter colors-to-tokens-plugin serve",
"build": "nx build plugins-runtime --emptyOutDir=true", "build": "pnpm --filter @penpot/plugins-runtime build",
"build:plugins": "nx run-many -t build --parallel -p tag:type:plugin --exclude=poc-state-plugin", "build:plugins": "pnpm --filter './apps/*-plugin' --filter '!poc-state-plugin' build",
"build:styles-example": "nx run example-styles:build", "build:styles-example": "pnpm --filter example-styles build",
"lint": "nx run-many --all --target=lint --parallel", "lint": "pnpm -r --parallel lint",
"format": "nx format:write --libs-and-apps", "format": "prettier --write \"**/*.{ts,tsx,js,jsx,json,md,html,css}\"",
"format:check": "nx format:check --libs-and-apps", "format:check": "prettier --check \"**/*.{ts,tsx,js,jsx,json,md,html,css}\"",
"lint:affected": "nx affected --target=lint", "test": "pnpm --filter @penpot/plugins-runtime --filter lorem-ipsum-plugin --filter colors-to-tokens-plugin test",
"test": "nx run-many -t test --parallel -p plugins-runtime lorem-ipsum-plugin colors-to-tokens-plugin", "test:e2e": "pnpm --filter e2e test",
"test:e2e": "nx test e2e",
"registry": "nx local-registry",
"build:doc": "typedoc --tsconfig libs/plugins-runtime/tsconfig.lib.json --customCss ./tools/typedoc.css --out dist/doc", "build:doc": "typedoc --tsconfig libs/plugins-runtime/tsconfig.lib.json --customCss ./tools/typedoc.css --out dist/doc",
"release": "tsx ./tools/scripts/publish.ts" "release": "tsx ./tools/scripts/publish.ts"
}, },
@ -41,24 +39,11 @@
"@angular/cli": "21.1.1", "@angular/cli": "21.1.1",
"@angular/compiler-cli": "21.1.1", "@angular/compiler-cli": "21.1.1",
"@angular/language-service": "21.1.1", "@angular/language-service": "21.1.1",
"@eslint/eslintrc": "^3.3.3",
"@eslint/js": "9.39.2", "@eslint/js": "9.39.2",
"@nx/angular": "22.4.0",
"@nx/devkit": "^22.4.0",
"@nx/esbuild": "22.4.0",
"@nx/eslint": "22.4.0",
"@nx/eslint-plugin": "22.4.0",
"@nx/js": "22.4.0",
"@nx/node": "22.4.0",
"@nx/vite": "22.4.0",
"@nx/vitest": "22.4.0",
"@nx/web": "22.4.0",
"@schematics/angular": "21.1.1", "@schematics/angular": "21.1.1",
"@swc-node/register": "1.11.1",
"@swc/core": "1.15.10",
"@swc/helpers": "0.5.18",
"@types/feather-icons": "^4.29.4", "@types/feather-icons": "^4.29.4",
"@types/node": "25.0.10", "@types/node": "25.0.10",
"@types/yargs": "^17.0.35",
"@typescript-eslint/eslint-plugin": "8.53.1", "@typescript-eslint/eslint-plugin": "8.53.1",
"@typescript-eslint/parser": "8.53.1", "@typescript-eslint/parser": "8.53.1",
"@typescript-eslint/utils": "^8.53.1", "@typescript-eslint/utils": "^8.53.1",
@ -80,9 +65,7 @@
"jiti": "2.6.1", "jiti": "2.6.1",
"jsdom": "~27.4.0", "jsdom": "~27.4.0",
"jsonc-eslint-parser": "^2.4.2", "jsonc-eslint-parser": "^2.4.2",
"nx": "22.4.0",
"prettier": "^3.8.1", "prettier": "^3.8.1",
"swc-loader": "0.2.7",
"tsx": "^4.21.0", "tsx": "^4.21.0",
"typedoc": "^0.28.16", "typedoc": "^0.28.16",
"typescript": "5.9.3", "typescript": "5.9.3",
@ -91,7 +74,9 @@
"vite-plugin-checker": "^0.12.0", "vite-plugin-checker": "^0.12.0",
"vite-plugin-dts": "4.5.4", "vite-plugin-dts": "4.5.4",
"vite-plugin-static-copy": "^3.1.5", "vite-plugin-static-copy": "^3.1.5",
"vitest": "4.0.17" "vite-tsconfig-paths": "^6.0.5",
"vitest": "4.0.17",
"yargs": "^17.7.2"
}, },
"dependencies": { "dependencies": {
"@angular/animations": "21.1.1", "@angular/animations": "21.1.1",
@ -111,8 +96,5 @@
"uuid": "^13.0.0", "uuid": "^13.0.0",
"zod": "^4.3.5", "zod": "^4.3.5",
"zone.js": "0.16.0" "zone.js": "0.16.0"
},
"nx": {
"includedScripts": []
} }
} }

7019
plugins/pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@ -1,5 +0,0 @@
{
"name": "penpot-plugins",
"$schema": "node_modules/nx/schemas/project-schema.json",
"targets": {}
}

View File

@ -1,56 +0,0 @@
import { CreateNodesV2, readJsonFile, logger } from '@nx/devkit';
import { createNodesFromFiles } from '@nx/devkit';
import { dirname } from 'path';
export const createNodesV2: CreateNodesV2 = [
'**/project.json',
async (configFiles, options, context) => {
return await createNodesFromFiles(
(configFile) => {
const projectConfiguration = readJsonFile(configFile);
if (
!projectConfiguration.tags ||
!projectConfiguration?.tags.includes('type:plugin') ||
!projectConfiguration?.targets.build
) {
return {};
}
const projectRoot = dirname(configFile);
return {
projects: {
[projectRoot]: {
root: projectRoot,
targets: {
init: {
executor: 'nx:run-commands',
options: {
command: `nx run-many --parallel --targets=buildPlugin,serve --projects=${projectConfiguration.name} --watch`,
},
},
buildPlugin: {
executor: '@nx/esbuild:esbuild',
outputs: ['{options.outputPath}'],
options: {
minify: true,
outputPath: `${projectConfiguration.sourceRoot}/assets/`,
main: `${projectConfiguration.sourceRoot}/plugin.ts`,
tsConfig: `${projectRoot}/tsconfig.plugin.json`,
generatePackageJson: false,
format: ['esm'],
deleteOutputPath: false,
},
},
},
},
},
};
},
configFiles,
options,
context,
);
},
];

View File

@ -1,8 +1,15 @@
import { execSync } from 'child_process'; import { execSync } from 'child_process';
import { releaseChangelog, releasePublish, releaseVersion } from 'nx/release'; import { readFileSync, writeFileSync } from 'fs';
import { join } from 'path';
import yargs from 'yargs'; import yargs from 'yargs';
import { hideBin } from 'yargs/helpers'; import { hideBin } from 'yargs/helpers';
const PACKAGES = [
'libs/plugin-types',
'libs/plugins-styles',
'libs/plugins-runtime',
];
const determineArgs = async () => { const determineArgs = async () => {
return await yargs(hideBin(process.argv)) return await yargs(hideBin(process.argv))
.version(false) .version(false)
@ -21,9 +28,9 @@ const determineArgs = async () => {
}) })
.option('version', { .option('version', {
description: description:
'Explicit version specifier to use, if overriding conventional commits', 'Explicit version specifier to use (e.g., 1.5.0, patch, minor, major)',
type: 'string', type: 'string',
default: undefined, demandOption: true,
}) })
.option('skip-publish', { .option('skip-publish', {
description: 'Skip publishing the package to the registry', description: 'Skip publishing the package to the registry',
@ -31,7 +38,7 @@ const determineArgs = async () => {
default: false, default: false,
}) })
.option('preid', { .option('preid', {
description: 'The prerelease identifier to use', description: 'The prerelease identifier to use (e.g., next, beta, alpha)',
type: 'string', type: 'string',
default: undefined, default: undefined,
}) })
@ -40,41 +47,170 @@ const determineArgs = async () => {
type: 'boolean', type: 'boolean',
default: true, default: true,
}) })
.option('first-release', {
description: 'Whether or not this is the first release',
type: 'boolean',
default: false,
})
.parseAsync(); .parseAsync();
}; };
interface PackageJson {
name: string;
version: string;
[key: string]: unknown;
}
const readPackageJson = (packagePath: string): PackageJson => {
const filePath = join(process.cwd(), packagePath, 'package.json');
return JSON.parse(readFileSync(filePath, 'utf-8'));
};
const writePackageJson = (packagePath: string, content: PackageJson): void => {
const filePath = join(process.cwd(), packagePath, 'package.json');
writeFileSync(filePath, JSON.stringify(content, null, 2) + '\n');
};
const incrementVersion = (
currentVersion: string,
specifier: string,
preid?: string,
): string => {
const [major, minor, patch] = currentVersion
.split('-')[0]
.split('.')
.map(Number);
switch (specifier) {
case 'major':
return preid ? `${major + 1}.0.0-${preid}.0` : `${major + 1}.0.0`;
case 'minor':
return preid
? `${major}.${minor + 1}.0-${preid}.0`
: `${major}.${minor + 1}.0`;
case 'patch':
return preid
? `${major}.${minor}.${patch + 1}-${preid}.0`
: `${major}.${minor}.${patch + 1}`;
case 'premajor':
return `${major + 1}.0.0-${preid || 'next'}.0`;
case 'preminor':
return `${major}.${minor + 1}.0-${preid || 'next'}.0`;
case 'prepatch':
return `${major}.${minor}.${patch + 1}-${preid || 'next'}.0`;
case 'prerelease': {
const preMatch = currentVersion.match(/-([^.]+)\.(\d+)$/);
if (preMatch) {
const preId = preid || preMatch[1];
const preNum = parseInt(preMatch[2], 10) + 1;
return `${major}.${minor}.${patch}-${preId}.${preNum}`;
}
return `${major}.${minor}.${patch + 1}-${preid || 'next'}.0`;
}
default:
// If specifier is an exact version (e.g., "1.5.0"), use it directly
if (/^\d+\.\d+\.\d+/.test(specifier)) {
return specifier;
}
throw new Error(`Unknown version specifier: ${specifier}`);
}
};
const log = (message: string, verbose: boolean, forceLog = false): void => {
if (verbose || forceLog) {
console.log(message);
}
};
(async () => { (async () => {
const args = await determineArgs(); const args = await determineArgs();
const result = await releaseVersion({ // Get current version from one of the packages
dryRun: args.dryRun, const currentPkg = readPackageJson(PACKAGES[0]);
specifier: args.version, const currentVersion = currentPkg.version;
gitCommit: false,
gitTag: false,
stageChanges: true,
verbose: args.verbose,
preid: args.preid,
});
execSync( const newVersion = incrementVersion(currentVersion, args.version, args.preid);
'npx nx run-many -t build -p plugins-runtime plugins-styles plugin-types --parallel',
{
cwd: process.cwd(),
stdio: 'inherit',
},
);
console.log(`\n📦 Release: ${currentVersion} → ${newVersion}`);
console.log(` Mode: ${args.dryRun ? 'DRY RUN' : 'REAL RELEASE'}`);
console.log(` Tag: ${args.latest ? 'latest' : 'next'}\n`);
// Update version in all packages
for (const packagePath of PACKAGES) {
const pkg = readPackageJson(packagePath);
const oldVersion = pkg.version;
pkg.version = newVersion;
// Update internal dependencies
const deps = pkg['dependencies'] as Record<string, string> | undefined;
if (deps && typeof deps === 'object') {
for (const dep of Object.keys(deps)) {
if (dep.startsWith('@penpot/')) {
deps[dep] = `^${newVersion}`;
}
}
}
log(` ${pkg.name}: ${oldVersion} → ${newVersion}`, args.verbose, true);
if (!args.dryRun) {
writePackageJson(packagePath, pkg);
}
}
console.log('\n🔨 Building packages...\n');
// Build all packages
if (!args.dryRun) {
execSync(
'pnpm --filter @penpot/plugins-runtime --filter @penpot/plugin-styles --filter @penpot/plugin-types build',
{
cwd: process.cwd(),
stdio: 'inherit',
},
);
} else {
console.log(' [DRY RUN] Skipping build\n');
}
// Publish packages
if (!args.skipPublish) { if (!args.skipPublish) {
await releasePublish({ console.log('\n📤 Publishing packages...\n');
dryRun: args.dryRun,
verbose: args.verbose, const tag = args.latest ? 'latest' : 'next';
tag: args.latest ? 'latest' : 'next',
}); for (const packagePath of PACKAGES) {
const pkg = readPackageJson(packagePath);
const distPath = join('dist', packagePath.split('/').pop()!);
if (args.dryRun) {
console.log(
` [DRY RUN] Would publish ${pkg.name}@${newVersion} with tag "${tag}"`,
);
} else {
try {
execSync(
`pnpm publish --tag ${tag} --access public --no-git-checks`,
{
cwd: join(process.cwd(), distPath),
stdio: args.verbose ? 'inherit' : 'pipe',
},
);
console.log(` ✅ Published ${pkg.name}@${newVersion}`);
} catch (error) {
console.error(` ❌ Failed to publish ${pkg.name}`);
throw error;
}
}
}
} else {
console.log('\n⏭️ Skipping publish (--skip-publish)\n');
}
console.log('\n✨ Release complete!\n');
if (!args.dryRun) {
console.log('Next steps:');
console.log(` 1. Review the changes`);
console.log(
` 2. Commit: git commit -am ":arrow_up: Updated plugins release to ${newVersion}"`,
);
console.log(` 3. Push: git push\n`);
} }
process.exit(0); process.exit(0);