Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rework reflections #4

Merged
merged 126 commits into from
Feb 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
126 commits
Select commit Hold shift + click to select a range
6df9895
Set package to be public
aedart Apr 24, 2023
89a14b1
Deprecate the reflector component
aedart Apr 24, 2023
40f29a9
Deprecate entire reflections package
aedart Apr 24, 2023
aad7429
Add reflections submodule in support package
aedart Apr 24, 2023
e7946d6
Remove reflections package
aedart Apr 24, 2023
036f9d0
Change release notes
aedart Apr 24, 2023
32d3ebd
Add support/reflections submodule in contracts package
aedart Apr 24, 2023
abd7028
Add Arrayable interface
aedart Apr 24, 2023
53fdc46
Cleanup
aedart Apr 24, 2023
da3dfb0
Add alias to reflections submodule, in contracts/support
aedart Apr 24, 2023
1564aea
Add Reflection interface
aedart Apr 24, 2023
71c0310
Add Kind enum
aedart Apr 24, 2023
3f3062b
Export Kind enum
aedart Apr 24, 2023
963d7d9
Change interface, add hasTarget util method
aedart Apr 24, 2023
e98597d
Mark contracts support/reflections as external
aedart Apr 24, 2023
6513dde
Fix type of name
aedart Apr 24, 2023
9a25bd5
Add Reflection
aedart Apr 24, 2023
a5b0aa9
Fix types
aedart Apr 24, 2023
b4ae271
Refactor Reflection, extract encoding and decoding into own component
aedart Apr 25, 2023
9f1929a
Add description
aedart Apr 25, 2023
b15f737
Rework reflection, remove toArray()
aedart Apr 26, 2023
ad744cb
Change Reflection, add target owner
aedart Apr 26, 2023
a03e490
Merge branch 'main' into rework-reflections
aedart Apr 28, 2023
856b900
Merge package.json files
aedart Apr 28, 2023
fa7a96d
Fix dual entry
aedart Apr 28, 2023
0cd2f77
Update dependencies
aedart Apr 28, 2023
9752780
Change reflection, return object or undefined instead of WeakRef
aedart Apr 30, 2023
3a52392
Change visibility to public for encode / decode kind methods
aedart Apr 30, 2023
06c91cd
Rename target to weak ref. util method
aedart Apr 30, 2023
cfc2a55
Refactor method
aedart Apr 30, 2023
2283130
Define "encoded" reflection object type
aedart Apr 30, 2023
b9d8aab
Fix return type of encode context method
aedart Apr 30, 2023
fa5e9da
Refactor resolve target owner util method
aedart Apr 30, 2023
434260c
Add symbol for "reflections", to be stored in metadata
aedart Apr 30, 2023
6ae6598
Add 1st draft of reflect decorator
aedart Apr 30, 2023
a54aeab
Export getReflection method
aedart May 1, 2023
2530cb6
Rework how reflection is stored and obtained
aedart May 1, 2023
d2c24b3
Add tests for class reflect (incomplete)
aedart May 1, 2023
1b7ab88
Fix style
aedart May 1, 2023
4b3ea0a
Rename test
aedart May 6, 2023
8c68249
Cleanup
aedart May 6, 2023
a1a4a3b
Add tests for method reflect
aedart May 6, 2023
dd53759
Cleanup reflect decorator
aedart May 6, 2023
2c8ea18
Refactor decorator, store reference to overwritten method
aedart May 6, 2023
7b35e39
Cleanup
aedart May 6, 2023
65843e7
Disable support for anything other than class and method
aedart May 6, 2023
31bec90
Deal with static / non-static elements with same name
aedart May 6, 2023
55b4705
Change is static value to number instead of string
aedart May 10, 2023
0db5840
Fix typo
aedart Jan 14, 2024
03fbbb8
Update dependencies
aedart Jan 25, 2024
1701a06
Ignore .nx directory
aedart Jan 25, 2024
ff29831
Fix babel tranfsform class static block missing
aedart Jan 25, 2024
a445e69
Change release notes
aedart Jan 25, 2024
bca79e9
Improve internal type hint
aedart Jan 25, 2024
9c91c08
Add toWeakRef util method
aedart Jan 25, 2024
88f8243
Deprecate toWeakReference()
aedart Jan 25, 2024
f6097e5
Change release notes
aedart Jan 25, 2024
78b3b62
Fix broken docs
aedart Jan 26, 2024
40d1325
Change release notes
aedart Jan 26, 2024
7f3443b
Remove unnecessary dependencies
aedart Jan 26, 2024
9f0da6f
Update installation guide for vuepress utils
aedart Jan 26, 2024
a48abc7
Add vuepress utils upgrade guide
aedart Jan 26, 2024
7380e67
Cleanup
aedart Jan 26, 2024
c3879c2
Shorten headings
aedart Jan 26, 2024
ba0fc68
Add available since badge for toWeakRef()
aedart Jan 26, 2024
dc04ae1
Add documentation of uniqueId and hasUniqueId util methods
aedart Jan 26, 2024
52b3897
Change release notes
aedart Jan 26, 2024
3491257
Bump license year
aedart Jan 26, 2024
0d34893
Change release notes
aedart Jan 26, 2024
6f8e616
Update dependencies
aedart Jan 28, 2024
c5782e6
Fix @vuepress/utils not resolved by Rollup
aedart Jan 28, 2024
e6d52b1
Change release notes
aedart Jan 28, 2024
f858e46
Deprecate reflections components
aedart Jan 28, 2024
a5bfc7e
Deprecate meta context related components
aedart Jan 28, 2024
93e0036
Add meta upgrade guide
aedart Jan 28, 2024
d341889
Remove PropertyKey
aedart Jan 28, 2024
039bb6e
Prevent attempt to overwrite context.metadata
aedart Jan 28, 2024
eaf6ae4
Change release notes
aedart Jan 28, 2024
6bf48f4
Add isPropertyKey util method
aedart Jan 28, 2024
c149880
Add docs for isPropertyKey
aedart Jan 28, 2024
39714bd
Change release notes
aedart Jan 28, 2024
ac5828e
Add isKey util method
aedart Jan 28, 2024
6e8d31f
Add docs for isKey
aedart Jan 28, 2024
9d902ba
Change release notes
aedart Jan 28, 2024
6fe8a39
Add available since badge
aedart Jan 28, 2024
ab5f71b
Remove unused type
aedart Jan 28, 2024
c023496
Fix explicit any lint
aedart Jan 28, 2024
4356b3b
Update dependencies
aedart Jan 29, 2024
071f194
Add mergeKeys() util method
aedart Jan 29, 2024
a4db593
Add docs for mergeKeys()
aedart Jan 29, 2024
e2f6c18
Change release notes
aedart Jan 29, 2024
eabd6c0
Deprecate the META_REFLECTIONS symbol
aedart Jan 29, 2024
37bee87
Cleanup
aedart Jan 29, 2024
258e307
Add isCallable and isClassConstructor (EXPERIMENTAL)
aedart Jan 30, 2024
35a6580
Add isConstructor util method
aedart Jan 30, 2024
5ab5a33
Add unit tests for isConstructor
aedart Jan 30, 2024
0e0f9aa
Export isConstructor
aedart Jan 30, 2024
f62acc9
Add docs for isConstructor()
aedart Jan 30, 2024
50cfc20
Change release notes
aedart Jan 30, 2024
508a3dc
Require node 20.11.0 as engine
aedart Jan 30, 2024
92d1417
Change release notes
aedart Jan 30, 2024
97b6c0a
Fix type
aedart Jan 30, 2024
7ad6a7e
Add array keys test
aedart Jan 31, 2024
187f899
Cleanup
aedart Jan 31, 2024
46af35a
Add array keys tests
aedart Jan 31, 2024
b0096c4
Improve JSDoc
aedart Jan 31, 2024
cffb55d
Add getMeta in method example
aedart Jan 31, 2024
c1b01a3
Add targetMeta() and getTargetMeta()
aedart Jan 31, 2024
05313ff
Change release notes
aedart Jan 31, 2024
4ffe8b2
Fix type of resolved key
aedart Jan 31, 2024
c87993c
Remove experimental reflect decorator
aedart Jan 31, 2024
4158625
Change release notes
aedart Jan 31, 2024
098926a
Remove caching mechanism
aedart Feb 1, 2024
06fc166
Add test of multiple target meta on same method
aedart Feb 1, 2024
7564166
Fix prefix key
aedart Feb 1, 2024
80ada0e
Add inheritTargetMeta() util method
aedart Feb 1, 2024
3cac44b
Change release notes
aedart Feb 1, 2024
68a9bc8
Optimise "target" prefix keys
aedart Feb 1, 2024
a6fec21
Cleanup
aedart Feb 1, 2024
3357acb
Improve method description
aedart Feb 2, 2024
f1c2b1d
Reorder changelog entries
aedart Feb 2, 2024
1a01b05
Add test of class instance when obtaining target meta
aedart Feb 2, 2024
d638228
Fix unable to obtain target meta for class instance
aedart Feb 2, 2024
b850b7c
Add docs for targetMeta() decorator
aedart Feb 2, 2024
70da6aa
Add description of how the targetMeta() decorator works behind the scene
aedart Feb 2, 2024
6afbfaf
Highlight target meta decorator feature
aedart Feb 2, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
fail-fast: true
matrix:
os: [ 'ubuntu-22.04' ]
node: [ '18' ]
node: [ '20' ]

steps:

Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
.idea/
Thumbs.db
temp/*
.nx

yarn.lock
lerna-debug.log
Expand Down
44 changes: 44 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,50 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added

* `targetMeta()`, `inheritTargetMeta()` and `getTargetMeta()` utils method in `@aedart/support/meta`.
* `isConstructor()` util method in `@aedart/support/reflections`.
* `isCallable()` and `isClassConstructor()` util methods in `@aedart/support/reflections` (_Unsafe / unstable!_).
* `Kind` enum which contains cases of the kind of element that is being decorated (_defined in a decorator context object_).
* `Arrayable` interface, in `@aedart/contracts/support` submodule.
* `toWeakRef()` util method in `@aedart/support/misc`.
* `mergeKets()` util method in `@aedart/support/misc`.
* `isKey()` util method in `@aedart/support/misc`.
* `isPropertyKey()` util method in `@aedart/support/misc`.
* Documentation for `uniqueId()` and `hasUniqueId()` util methods, in the `@aedart/support/objects` package.

### Changed

**Breaking**

* Node `^v20.11.0` is now required when working with the ion mono-repository.
* Decorator `Context` is now an alias for TypeScript's `DecoratorContext` (_affects `@aedart/support/meta`_).
* Decorator `MetadataRecord` is now an alias for TypeScript's `DecoratorMetadata` (_affects `@aedart/support/meta`_).

**Non-Breaking**

* Bumped license year.
* Dependencies updated (_service update_).
* Refactored internal `save()` method to no longer attempt to overwrite `context.metadata` because it has been defined as read-only property by TypeScript.
* JSDoc now clearly states that `meta()` is intended to be used as a decorator.

### Removed

* Private `@aedart/reflections` package. Desired features added as a submodule in `@aedart/support` package.
* Experimental reflection components in `@aedart/support/reflections` submodule (_was never published_).
* `PropertyKey` from `@aedart/contacts/support` (_Replaced by TypeScript's own definition hereof_).

### Deprecated

* `ClassContext`, `MethodContext`, `GetterContext`, `SetterContext`, `FieldContext`, `AccessorContext` and `MetadataContext`, in `@aedart/contracts/support/meta` - replaced by corresponding TypeScript declarations and will be removed in next version.
* `MemberContext` in `@aedart/contracts/support/meta` (_no longer needed_).

### Fixed

* Docs broken due to out-of-date vuepress dependencies (_switched to `@vuepress` `v2.0.0-rc.2`_).
* `@vuepress/utils` not resolved by rollup during tests (_missing dependency, in the `@aedart/vuepress-utils` package_).

## [0.6.1] - 2023-04-28

### Security
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Copyright (c) 2023-2023 Alin Eugen Deac <[email protected]> . All rights reserved.
Copyright (c) 2023-2024 Alin Eugen Deac <[email protected]> . All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

Expand Down
1 change: 1 addition & 0 deletions aliases.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ module.exports = {
alias: {
// contracts
'@aedart/contracts/support/meta': path.resolve(__dirname, './packages/contracts/support/meta'),
'@aedart/contracts/support/reflections': path.resolve(__dirname, './packages/contracts/support/reflections'),
'@aedart/contracts/support': path.resolve(__dirname, './packages/contracts/support'),
'@aedart/contracts': path.resolve(__dirname, './packages/contracts/src'),

Expand Down
32 changes: 19 additions & 13 deletions babel.config.json
Original file line number Diff line number Diff line change
@@ -1,16 +1,22 @@
{
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "entry",
"corejs": "3.22"
}
"presets": [
[
"@babel/preset-env",
{
"useBuiltIns": "entry",
"corejs": "3.22"
}
]
],
"plugins": [
[
"@babel/plugin-proposal-decorators",
{
"version": "2023-01"
}
],
"@babel/plugin-proposal-class-properties",
"@babel/plugin-proposal-private-methods",
"@babel/plugin-transform-class-static-block"
]
],
"plugins": [
["@babel/plugin-proposal-decorators", { "version": "2023-01" }],
"@babel/plugin-proposal-class-properties",
"@babel/plugin-proposal-private-methods"
]
}
1 change: 1 addition & 0 deletions docs/.vuepress/archive/Version0x.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ export default PagesCollection.make('v0.x', '/v0x', [
'packages/support/install',
'packages/support/meta',
'packages/support/objects',
'packages/support/reflections',
'packages/support/misc',
]
},
Expand Down
10 changes: 8 additions & 2 deletions docs/.vuepress/config.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import {defaultTheme, defineUserConfig, Page} from 'vuepress';
import {defineUserConfig, Page} from 'vuepress';
import defaultTheme from "@vuepress/theme-default"
import { webpackBundler } from "@vuepress/bundler-webpack"
import {backToTopPlugin} from "@vuepress/plugin-back-to-top";
import {searchPlugin} from "@vuepress/plugin-search";
import {baseURL, prefixPath} from "@aedart/vuepress-utils";
Expand All @@ -16,6 +18,10 @@ const BASE_URL = baseURL('ion');
* Vuepress configuration for docs...
*/
export default defineUserConfig({
bundler: webpackBundler({
// N/A
}),

base: BASE_URL,
dest: './.build',
lang: 'en-GB',
Expand Down Expand Up @@ -71,7 +77,7 @@ export default defineUserConfig({
},

getExtraFields: (page: Page) => {
return [page.frontmatter.description] ?? [];
return [page.frontmatter.description];
},
}),

Expand Down
22 changes: 22 additions & 0 deletions docs/archive/current/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,28 @@ _TBD: "To be decided"._

## `v0.x` Highlights

### "Target" Meta Decorator <Badge type="tip" text="Available since v0.7" />

Associate arbitrary metadata directly with the target element that is being decorated.
_See [target meta decorator](./packages/support/meta.md) fro additional details._

```js
import {targetMeta, getTargetMeta} from '@aedart/support/meta';

class Service {

@targetMeta('desc', 'Seaches for cities')
search() {
// ...not shown...
}
}

const instance = new Service();

// ...later in your application...
getTargetMeta(instance.search, 'desc'); // Seaches for cities
```

### Meta Decorator <Badge type="tip" text="Available since v0.6" />

The [meta decorator](./packages/support/meta.md) is able to associate arbitrary metadata with a class and its elements.
Expand Down
183 changes: 180 additions & 3 deletions docs/archive/current/packages/support/meta.md
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ import {meta, getMeta} from '@aedart/support/meta';
@meta('service_alias', 'locationSearcher')
class Service {}

getMeta(Service, 'service_alias');
getMeta(Service, 'service_alias'); // locationSearcher
```

**Roughly "desugars" to the following:**
Expand All @@ -240,10 +240,187 @@ function meta(key, value) {
@meta('service_alias', 'locationSearcher')
class Service {}

Service[Symbol.metadata].service_alias;
Service[Symbol.metadata].service_alias; // locationSearcher
```
(_Above shown example is very simplified. Actual implementation is a bit more complex..._)

At present, the internal mechanisms of the `meta` decorator must rely on a [`WeakMap`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap) to associate metadata with the intended class.
When the [Decorator Metadata proposal](https://github.com/tc39/proposal-decorator-metadata) becomes more mature and transpilers offer the `context.metadata` object (_or when browsers support it_),
then this decorator will be updated respectfully to use the available metadata object.
then this decorator will be updated respectfully to use the available metadata object.

## Target Meta <Badge type="tip" text="Available since v0.7" vertical="middle" />

The `targetMeta()` decorator offers the ability to associate metadata directly with a class instance or class method reference.
This can be useful in situations when you do not know the class that owns the metadata.

Behind the scene, `targetMeta()` uses the `meta()` decorator and stores a reference to the target that is decorated inside a [`WeakMap`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/WeakMap).

::: tip Supported Elements

Unlike the [`meta()` decorator](#supported-elements), `targetMeta()` only supports the following elements:

* `class`
* `method`

:::

**Example: class instance**

```js
import {targetMeta, getTargetMeta} from '@aedart/support/meta';

@targetMeta('description', { type: 'Search Service', alias: 'Location Sercher' })
class LocationSearcherService {}

const instance = new LocationSearcherService();

// ...later in your application...
getTargetMeta(instance, 'description')?.type; // Search Service
```

**Example: method reference**

```js
import {targetMeta, getTargetMeta} from '@aedart/support/meta';

class LocationSearcherService {

@targetMeta('dependencies', [ 'httpClient' ])
search(apiClient) {}
}

const instance = new LocationSearcherService();

// ...later in your application...
getTargetMeta(instance.search, 'dependencies'); // [ 'httpClient' ]
```

### Inheritance

Target meta is automatically inherited by subclasses and can also be overwritten, similar to that of the [`meta()` decorator](#inheritance).

**Example: classes**

```js
import {targetMeta, getTargetMeta} from '@aedart/support/meta';

@meta('service_alias', 'locationSearcher')
class Service {}

class CitySearcher extends Service {}

const instance = new CitySearcher();

// ...later in your application...
getTargetMeta(instance, 'service_alias'); // locationSearcher
```

**Example: methods**

```js
import {targetMeta, getTargetMeta} from '@aedart/support/meta';

class Service {

@targetMeta('dependencies', [ 'countrySearchApiClient' ])
search(apiClient) {
// ...not shown...
}
}

class CountrySearcher extends Service {
// ... not method overwrite here...
}

class CitySearcher extends Service {

@targetMeta('dependencies', [ 'citySearchApiClient' ])
search(apiClient) {
// ...not shown...
}
}

const instanceA = new Service();
const instanceB = new CountrySearcher();
const instanceC = new CitySearcher();

// ...later in your application...
getTargetMeta(instanceA.search, 'dependencies'); // [ 'countrySearchApiClient' ]
getTargetMeta(instanceB.search, 'dependencies'); // [ 'countrySearchApiClient' ]
getTargetMeta(instanceC.search, 'dependencies'); // [ 'citySearchApiClient' ]
```

#### Static Methods

Inheritance for static methods works a bit differently. By default, any subclass will automatically inherit target metadata, even for static methods.
However, if you overwrite the given static method, the metadata is lost.

::: tip Limitation

_When a static method is overwritten, the parent's "target" metadata cannot be obtained due to a general limitation of the `meta()` decorator.
The decorator has no late `this` binding available to the overwritten static method.
This makes it impossible to associate the overwritten static method with metadata from the parent._

:::

**Example: inheritance for static methods**

```js
import {targetMeta, getTargetMeta} from '@aedart/support/meta';

class Service {

@targetMeta('dependencies', [ 'xmlClient' ])
static search(client) {
// ...not shown...
}
}

class CountrySearcher extends Service {
// ... not method overwrite here...
}

class CitySearcher extends Service {

// Overwite of static method - target meta is lost
static search(client) {}
}

// ...later in your application...
getTargetMeta(CountrySearcher.search, 'dependencies'); // [ 'xmlClient' ]
getTargetMeta(CitySearcher.search, 'dependencies'); // undefined
```

To overcome the above shown issue, you can use the `inheritTargetMeta()` decorator. It forces the static method to "copy" metadata from its parent, if available.

**Example: force inheritance for static methods**

```js
import {
targetMeta,
getTargetMeta,
inheritTargetMeta
} from '@aedart/support/meta';

class Service {

@targetMeta('dependencies', [ 'xmlClient' ])
static search(client) {
// ...not shown...
}
}

class CountrySearcher extends Service {
// ... not method overwrite here...
}

class CitySearcher extends Service {

@inheritTargetMeta()
static search(client) {}
}

// ...later in your application...
getTargetMeta(CountrySearcher.search, 'dependencies'); // [ 'xmlClient' ]
getTargetMeta(CitySearcher.search, 'dependencies'); // [ 'xmlClient' ]
```
Loading
Loading