Postcss 8 plugin: How to avoid loop into Declaration function? - postcss

Hello postcss experts!
I’m updating an old plugin to postCSS 8 API but I meet some issues.
This simple postCSS plugin fall into an infinite loop:
module.exports = (options = {}) => {
return {
postcssPlugin: 'postcss-failing-plugin',
Declaration(decl) {
if (decl.prop.startsWith('--')) {
decl.prop = decl.prop.replace(/^--/, `--prefix-`);
}
},
};
};
module.exports.postcss = true;
The documentation mention this behaviour:
Plugins will re-visit all nodes, which you changed or added. If you will change any children, plugin will re-visit parent as well. Only Once and OnceExit will not be called again.
writing a plugin
But nothing to avoid it.
How to edit a value in Declaration without making an infinite loop?

You may be repeatedly adding a prefix to custom property declarations that are already prefixed, causing the declaration visitor to run infinitely.
You can use a negative lookahead assertion (?!) to match custom properties that do not begin with a specific custom property prefix, i.e. ^--(?!prefix-).
const matcher = /^--(?!prefix-)/
const replacement = '--prefix-'
const ensure = value => value.replace(matcher, replacement)
// these _should not_ receive a new prefix
ensure('foo') // "foo"
ensure('prefix-foo') // "prefix-foo"
ensure('--prefix-foo') // "--prefix-foo"
// these _should_ receive a new prefixed
ensure('--foo') // "--prefix-foo"
ensure('--prefixable-foo') // "--prefix-prefixable-foo"
As applied to your example
module.exports = (options = {}) => {
return {
postcssPlugin: 'postcss-failing-plugin',
Declaration(decl) {
/** Matches a `--` property not beginning with `--prefix-`. */
const match = /^--(?!prefix-)/
if (match.test(decl.prop)) {
decl.prop = decl.prop.replace(match, `--prefix-`);
}
},
};
};
module.exports.postcss = true;

Related

React - useState appending to the array changes previously appended objects

Hi all,
I have a small react app that is creating (mapping from an array) new tabs (and panels) when there is a new message over the websocket.
There is an initial setup, that is hardcoded for the test purposes which sets up 2 tabs on load, any new ones should be appended to these two.
const INITIAL_ARRAY= [
{
id: 1,
child_component_config: {...}
},
{
id: 2,
child_component_config: {...}
}
];
const template = {
child_component_config: {...}
}
The code, simplifed:
const [current_array, setNewArray] = useState( INITIAL_ARRAY);
export default function ParentComponent() {
useEffect(() => {
const client = new ws...
client.onConnect = function (frame) {
var message = clientDesk.subscribe( '/topic/desks/4', function (message) {
// on message
var new_tab = TEMPLATE;
new_tab.id = Math.max( ...current_array.map( elem => elem.id ) ) + 1;
setActiveTab(new_tab.id);
setNewArray([...current_array, new_tab]);
}
}
}, [ current_array ]);
const tabs = map tabs
cosnt panels = map panels
return(
{tabs}
{panels}
)
The problem:
On first message from the WS the third element is added to the array properly (example-1)
, fourth one is added properly but it also overwrites the third element (example-2)
making them exactly the same. After fourth it gets strange, where some are overwritten and some are not.
I've tried:
Moving the state updating out of useEffect or removing useEffect completly
Storing current_array in a temp var before any logic
Adding a counter to track which tab's id is the latest -> tracking state of just one number works
const [tab_count, setTabCount] = useState( INITIAL_ARRAY.lenght );
Using counter to try to force rendering
Setting up a fixed number of objects in the initial array and just update which ever is needed (with and without counter)
Updating based on the previous value
setNewArray( prevArray => {
logic
return [...prevArray, new_tab];
}
After the first WS message, if the code is changed/saved and webpack compiled, the next message will add a new element to the array properly.
EDIT - Solved:
Managed to solve this by building a new object (instead of using the template) before adding it to the array.

How can we override Ext.Base?

I am using Ext JS v7.1 and I have overridden Ext.Base to set my naming scheme for the classes that inherits from Ext.Base: This eases my debugging.
Ext.define('App.class.Base', {
override: 'Ext.Base',
constructor: function() {
var me = this
/**
* App.base.store.Base => store-base-
* App.store.Menu => store-menu-
*/
if (me.isIdentifiable) {
if (!me.self.prototype.hasOwnProperty('identifiablePrefix')) {
const classNameParts = me.$className.match(/([^\.]+)/g)
if (classNameParts && classNameParts[0] === 'App') {
classNameParts.splice(0, classNameParts.length - 2)
me.self.prototype.identifiablePrefix = classNameParts.reduce((i, j) => i + '-' + j).toLocaleLowerCase() + '-'
}
}
}
return me.callParent()
}
})
This code was building before without an error but, after I upgraded Sencha Cmd to v7.3.0.19, I started the get the following error:
[ERR] C2016: Override target not found -- /...../packages/local/module-core/overrides/class/Base.js:2:64
[WRN] Override App.class.Base in file /..../packages/local/module-core/overrides/class/Base.js had no target detected
I don't know whether this is the right place/way to do this override, if not I can change my implementation. However, if there is no other way, how can get rid of the build error?
Thanks in advance,
Ipek
Because i am not using sencha build tools anymore, i can not help you directly but i would like to share another approach:
In case you have loaded the framework (ext-debug-all or ext-all, etc.) first and the class which should get overwritten is already defined you can do it like that:
Ext.Component.override({
initComponent: function () {
Ext.log('bootstraping ' + this.self.getName());
var me = this,
width = me.width,
height = me.height;
// If plugins have been added by a subclass's initComponent before calling up to here (or any components
// that don't have a table view), the processed flag will not have been set, and we must process them again.
// We could just call getPlugins here however most components don't have them so prevent the extra function call.
if (me.plugins && !me.plugins.processed) {
me.plugins = me.constructPlugins();
}
me.pluginsInitialized = true;
// this will properly (ignore or) constrain the configured width/height to their
// min/max values for consistency.
if (width != null || height != null) {
me.setSize(width, height);
}
if (me.listeners) {
me.on(me.listeners);
me.listeners = null; //change the value to remove any on prototype
}
if (me.focusable) {
me.initFocusable();
}
}
});
Depending on the further internal processing you can call callParent or callSuper.
More details here:
https://docs.sencha.com/extjs/6.5.3/classic/Ext.Class.html#cfg-override
You may be able to move this upper code inside a function and call it later, for example - when Ext.isReady. I guess this can solve or tackle some of the open tooling issues you are facing.
UPDATE:
Coming back to your question you can do the following and define it like that:
Ext.Base.override({
constructor: function() {
var me = this
/**
* App.base.store.Base => store-base-
* App.store.Menu => store-menu-
*/
if (me.isIdentifiable) {
if (!me.self.prototype.hasOwnProperty('identifiablePrefix')) {
const classNameParts = me.$className.match(/([^\.]+)/g)
if (classNameParts && classNameParts[0] === 'App') {
classNameParts.splice(0, classNameParts.length - 2)
me.self.prototype.identifiablePrefix = classNameParts.reduce((i, j) => i + '-' + j).toLocaleLowerCase() + '-'
}
}else{
console.log('isIdentifiable');
console.log(me.identifiablePrefix);
}
}
return me.callParent(arguments)
}
});
I have added an exampole fiddle here. It should log "helloWorld" in case identifiablePrefix is set.
https://fiddle.sencha.com/#view/editor&fiddle/3a8i

Ace-Editor Ignore/whitelist some tag/string

Is there any way to avoid Ace-Editor to raise an error if I use some tag like <#input "asd">?? Like a whitelist to suggest AceEditor to ignore it... Anyway, It's an internal key and I cannot avoid using it.
I'm using react.
Thanks
I'm thinking of two ways to solve the issue:
1) Maybe you can bind the ">", get the last entered values and then clear the errors:
editor.commands.addCommand({
name: "dotCommand1",
bindKey: { win: ".", mac: "."},
exec: function () {
var pos = editor.selection.getCursor();
var session = editor.session;
var curLine = (session.getDocument().getLine(pos.row)).trim();
var curTokens = curLine.slice(0, pos.column).split(/\s+/);
//You can build a logic using the curTokens array and then when you find <#input "asd"> clear the errors.
// If we assume curTokens[0] to have the value
if(curTokens[0] === '<#input "asd">') {
editor.session.setAnnotations([]); // This would remove the being shown error
}
}
});
2) Use the on change event and use the above similar logic and then clear the error
editor.getSession().on('change', function () {
// same logic as above
})

Custom attributes are removed when using custom blots

I created a custom blot for links that requires to be able to set rel and target manually. However when loading content that has those attributes, quill strips them. I'm not sure why.
I created a codepen to illustrate the issue.
This is my custom blot:
const Inline = Quill.import('blots/inline')
class CustomLink extends Inline {
static create(options) {
const node = super.create()
node.setAttribute('href', options.url)
if (options.target) { node.setAttribute('target', '_blank') }
if (options.follow === 'nofollow') { node.setAttribute('rel', 'nofollow') }
return node
}
static formats(node) {
return node.getAttribute('href')
}
}
CustomLink.blotName = 'custom_link'
CustomLink.tagName = 'A'
Quill.register({'formats/custom_link': CustomLink})
Do I have to tell Quill to allow certain atttributes?
Upon initialization from existing HTML, Quill will try to construct the data model from it, which is the symmetry between create(), value() for leaf blots, and formats() for inline blots. Given how create() is implemented, you would need formats() to be something like this:
static formats(node) {
let ret = {
url: node.getAttribute('href'),
};
if (node.getAttribute('target') == '_blank') {
ret.target = true;
}
if (node.getAttribute('rel') == 'nofollow') {
ret.follow = 'nofollow';
}
return ret;
}
Working fork with this change: https://codepen.io/quill/pen/xPxGgw
I would recommend overwriting the default link as well though instead of creating another one, unless there's some reason you need both types.

Javascript and Typescript: iteration over object with for..of

From ES6 iteration over object values I took a way of iteration over object values. Here's my implementation:
function* entries (obj): IterableIterator<any> {
for (const key of Object.keys(obj)) {
const value = obj[key];
yield [
key,
value,
];
}
}
Then I want to use that in render() React Component's method:
render () {
const that = this;
const newVar = (() => {
const elements = [];
for (const [key, value] of entries(that.filters)) {
elements.push(<h2>{JSON.stringify(key)} {JSON.stringify(value)}</h2>);
}
return elements;
})();
return (
<section>
{newVar}
</section>
);
}
But if I debug this code with .map files in Chrome Developer console, newVar is an empty array. If I alert obj in entries generator I got proper object. Am I missing something really simple? Thank you in advance for every answer. I use Typescript with es5 option and I read that it supports generators: Generators and Iteration for ES5/ES3.
I use Typescript with es5 option and I read that it supports generators: Generators and Iteration for ES5/ES3.
It also says that you need to use the --downlevelIteration flag for that. Otherwise it will still transpile for … of-loops into array iteration, which fails on iterables like your generator instance without a .length property.

Resources