Spectral 2.x Upgrade Guide
Some syntactical changes were made between Spectral 1.x and Spectral 2.x in order to improve the developer experience and catch some common errors at compile time (rather than at runtime). Let's look at those changes, and how to go about upgrading a component from 1.x to 2.x. To see an example of upgrading a component from Spectral 1.x to 2.x, this commit upgrades the "Format Name" example component from the writing custom components article.
To start, update @prismatic-io/spectral
in your package.json
file and then run npm install
or yarn install
:
{
"dependencies": {
"@prismatic-io/spectral": "^2.0.0"
}
}
Input Keys Have Moved
Motivation: In Spectral 1.x, each input
had a key
attribute to uniquely identify it.
This caused problems, though, as the value of the input
's key
had to exactly match the action
's perform function's params.keyName
in order to reference inputs correctly.
Mismatched input keys and parameter keys would yield unknown
values for inputs.
Typescript generics were used in 2.x to ensure that your editor and compiler catch mismatched keys prior to deployment.
First, remove the key:
property of each input:
const myFirstInput = input({
/* key: "first", */ // Removed in spectral 2.x
label: "My Input Field",
placeholder: "Some example input",
type: "string",
required: true,
});
const mySecondInput = input({
/* key: "second", */
label: "My Input Field",
placeholder: "Some example input",
type: "string",
required: true,
});
Inputs on actions are now an object (key-value pairs) instead of an array. Adjust your actions accordingly:
/* Spectral 1.x */
const myAction = action({
perform: async (context, { first, second }) => {
/*...*/
},
inputs: [myFirstInput, mySecondInput],
});
/* Spectral 2.x */
const myAction = action({
perform: async (context, { first, second }) => {
/*...*/
},
inputs: {
first: myFirstInput,
second: mySecondInput,
},
});
Ensure that your destructured params
parameter (in this case, { first, second }
) has keys that match the keys you provide in the inputs
object.
If they do not match, your compiler will throw an error.
Action Keys Have Moved
Similar to inputs, action keys have also moved.
This addresses an issue in Spectral 1.x where if an action's key
property did not match the name of the variable representing the action
, a component would compile but component's action code would not be found at runtime.
Action's unique keys are now declared as part of the component
declaration, and the key
property has been removed from the action
declaration:
/* Spectral 1.x */
const myAction = action({
key: "myAction", // Remove this
/*...*/
});
export default component({
/*...*/
actions: {
// Remove the spread operators
...myAction,
...myOtherAction,
},
});
/* Spectral 2.x */
const myAction = action({
/*...*/
});
export default component({
/*...*/
actions: {
myAction: myAction,
myOtherAction: myOtherAction,
},
});
Note: you can use JavaScript shorthand property names to instead write actions: { myAction, myOtherAction }
.
Component Version is Deprecated
Component versioning is handled by the platform, so there's no need to pass in a version
property in your component
declaration.
You can remove it.
export default component({
key: "my-component",
/* version: "123", */
});
Input Types are More Strict
In Spectral 1.x, all inputs had an any
type.
So, if a helper function expected (string, number)
inputs, this sort of invocation would be acceptable by TypeScript with Spectral 1.x:
const helper = (foo: string, bar: number) =>
`Hi, ${foo}, adding 3 gives you ${bar + 3}`;
const myAction = action({
perform: async (context, { myInput1, myInput2 }) => {
helper(myInput1, myInput2);
},
});
This caused runtime issues.
What if a component's user entered the string "2"
for myInput2
?
That would cause problems, since "2" + 3
is equal to "23"
in JavaScript.
That's probably not the desirable result.
In Spectral 2.x we changed all inputs to have an unknown
type, so you need to explicitly convert inputs into the type you expect.
We created a series of utility functions to help convert inputs to types your helper functions and third-party libraries require.
The above code could be written using util.types
functions:
const helper = (foo: string, bar: number) =>
`Hi, ${foo}, adding 3 gives you ${bar + 3}`;
const myAction = action({
perform: async (context, { myInput1, myInput2 }) => {
helper(util.types.toString(myInput1), util.types.toNumber(myInput2));
},
});
If a string "2"
is input into myInput2
using Spectral 2.x now, that string is converted to a number and then passed into helper
, and the result would be 5
as opposed to "23"
.
Unit Testing
In Spectral 2.x, it is assumed that you want a PerformDataReturn
object when you run an invoke
function in a unit test.
Passing the return object type to the invoke function is no longer required.
/* Spectral 1.x */
await invoke<PerformDataReturn>(myAction, { someInput });
/* Spectral 2.x */
await invoke(myAction, { someInput });